Scala列表归约问题:列表转单列表及键值对列表处理
Scala列表归约相关问题解答
问题1:将列表的列表归约为单个列表
这个需求在Scala里有几种简洁的实现方式,根据场景选就行:
用
flatten方法(最直接):这是Scala为嵌套列表专门提供的展开方法,能一键把外层列表里的所有子列表元素合并成一个单一列表。val listOfLists = List(List(1, 2), List(3, 4), List(5)) val flattenedList = listOfLists.flatten // 输出结果: List(1, 2, 3, 4, 5)用归约操作(
foldLeft/reduce):如果需要手动控制合并逻辑,比如自定义合并规则,可以用foldLeft从空列表开始逐个合并子列表;reduce则更简洁,但要注意原列表为空时会抛出异常,所以foldLeft更安全。// foldLeft实现,兼容空列表场景 val mergedWithFold = listOfLists.foldLeft(List.empty[Int])(_ ++ _) // reduce实现,仅当listOfLists非空时可用 val mergedWithReduce = listOfLists.reduce(_ ++ _)
问题2:归约多组(name, value)列表,统计最频繁值及占比
你已经完成了最核心的分组统计步骤,接下来只需要对每个分组的结果做进一步处理,就能得到目标输出,咱们一步步来完善:
步骤1:合并所有输入列表
首先把多组(name, value)列表合并成一个单一列表,方便后续统一处理:
val inputLists = List( List(('a',1), ('b',2), ('c',4)), List(('a',2), ('b',3), ('c',4)), List(('a',1), ('b',3), ('c',4)) ) val combinedList = inputLists.flatten
步骤2:基于你的分组代码继续处理
你已经写出了这段代码,用来统计每个name下各value的出现次数:
val grouped = combinedList .groupBy(_._1) .mapValues(_.groupBy(_._2).mapValues(_.size)) .toList .map(x => (x._1, x._2.toList)) // 输出结果: List((b,List((2,1), (3,2))), (a,List((2,1), (1,2))), (c,List((4,3))))
接下来我们对每个(name, List((value, count)))元组做三件事:
- 计算当前name对应的总出现次数(所有value的count之和)
- 找到出现次数最多的value(若有多个最大值,任选其一即可)
- 计算该value的出现占比(次数/总次数)
把这些逻辑整合到代码里:
val result = grouped.map { case (name, valueCounts) => // 计算当前name的总出现次数 val totalCount = valueCounts.map(_._2).sum // 找到最大的出现次数 val maxFreq = valueCounts.map(_._2).max // 筛选出出现次数等于maxFreq的项,取第一个(多最大值场景任选其一) val (mostFreqValue, freq) = valueCounts.find(_._2 == maxFreq).get // 计算占比,这里保留两位小数,你可以根据需求调整精度 val ratio = BigDecimal(freq.toDouble / totalCount).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble // 组合成目标格式的元组 (name, mostFreqValue, ratio) } // 输出结果: List((b,3,0.67), (a,1,0.67), (c,4,1.0))
简化版:链式调用整合所有步骤
你也可以把整个流程写成链式调用,代码更紧凑:
val finalResult = inputLists.flatten .groupBy(_._1) .mapValues { entries => val valueCounts = entries.groupBy(_._2).mapValues(_.size).toList val total = valueCounts.map(_._2).sum val maxFreq = valueCounts.map(_._2).max val (value, freq) = valueCounts.find(_._2 == maxFreq).get (value, BigDecimal(freq.toDouble / total).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble) } .toList .map { case (name, (value, ratio)) => (name, value, ratio) }
这样就能得到你期望的输出啦,示例里的0.66和这里的0.67差异是因为四舍五入规则,你可以根据需求调整精度或者保留原始小数。
内容的提问来源于stack exchange,提问作者yalkris
相关产品推荐
相关产品推荐

