我在Scala中有两个列表,如何合并它们以便将元组组合在一起?
是否有现成的Scala列表API可以执行此操作或需要我自己执行此操作?
输入:
List((a,4), (b,1), (c,1), (d,1))
List((a,1), (b,1), (c,1))
预期产出:
List((a,5),(b,2),(c,2),(d,1))
答案 0 :(得分:19)
您可以尝试以下一行:
scala> ( l1 ++ l2 ).groupBy( _._1 ).map( kv => (kv._1, kv._2.map( _._2).sum ) ).toList
res6: List[(Symbol, Int)] = List(('a,5), ('c,2), ('b,2), ('d,1))
l1
和l2
是您想要合并的元组列表。
现在,细分:
(l1 ++ l2)
您只是连接两个列表.groupBy( _._1)
您按照第一个元素对所有元组进行分组。您将收到一张地图
作为键的第一个元素和以此元素作为值开头的元组列表。.map( kv => (kv._1, kv._2.map( _._2).sum ) )
您使用类似的键制作新地图,但值是所有第二个元素的总和。.toList
您将结果转换回列表。或者,您可以使用模式匹配来访问元组元素。
( l1 ++ l2 ).groupBy( _._1 ).map{
case (key,tuples) => (key, tuples.map( _._2).sum )
}.toList
答案 1 :(得分:3)
或者,您也可以使用mapValues
来缩短代码。
mapValues
允许您仅重新映射groupBy
创建的地图中每个(键,值)对的值。
在这种情况下,传递给mapValues
的函数将每个(Char,Int)元组减少为Int,然后对得到的Ints列表求和。
(l1 ::: l2).groupBy(_._1).mapValues(_.map(_._2).sum).toList
如果输出列表的顺序需要遵循您的示例,则只需添加依赖于Ordering [(Char,Int)]隐式实例的sorted
。
(l1 ::: l2).groupBy(_._1).mapValues(_.map(_._2).sum).toList.sorted
答案 2 :(得分:0)
如果你可以假设List[(A,B)]
根据Ordering[A]
排序{/ 1}},你可以这样写:
def mergeLists[A,B](one:List[(A,B)], two:List[(A,B)])(op:(B,B)=>B)(implicit ord:Ordering[A]): List[(A,B)] = (one,two) match {
case (xs, Nil) => xs
case (Nil, ys) => ys
case((a,b)::xs,(aa,bb)::ys) =>
if (a == aa) (a, op(b,bb)) :: mergeLists(xs,ys)(op)(ord)
else if (ord.lt(a,aa)) (a, b) :: mergeLists(xs, (aa,bb)::ys)(op)(ord)
else (aa, bb) :: mergeLists((a,b) :: xs, ys)(op)(ord)
}
不幸的是,这不是尾递归。