How to merge two lists of tuples?
我在 Scala 中有两个列表,如何合并它们以使元组组合在一起?
是否有现有的 Scala 列表 API 可以执行此操作或需要我自己执行?
输入:
1 2 | List((a,4), (b,1), (c,1), (d,1)) List((a,1), (b,1), (c,1)) |
预期输出:
1 | List((a,5),(b,2),(c,2),(d,1)) |
你可以试试下面的一行:
1 2 | 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) 你只是连接两个列表 -
.groupBy( _._1) 您按第一个元素对所有元组进行分组。您将收到一张地图,其中包含
第一个元素作为键和以该元素开头的元组列表作为值。 -
.map( kv => (kv._1, kv._2.map( _._2).sum ) ) 您制作了一个新地图,具有相似的键,但值是所有第二个元素的总和。 -
.toList 将结果转换回列表。
或者,您可以使用模式匹配来访问元组元素。
1 2 3 |
或者,您也可以使用
在这种情况下,传递给
1 | (l1 ::: l2).groupBy(_._1).mapValues(_.map(_._2).sum).toList |
如果输出列表的顺序需要遵循您的示例,只需添加依赖于 Ordering[(Char, Int)] 隐式实例的
1 | (l1 ::: l2).groupBy(_._1).mapValues(_.map(_._2).sum).toList.sorted |
使用
从一个列表中获取一张地图,然后遍历第二个列表。我们将条目更新到地图中。
1 2 3 | l1.foldLeft(l2.toMap)((accumulator, tuple) => accumulator + (tuple._1 -> (accumulator.getOrElse(tuple._1, 0) + tuple._2)) ).toList |
结果为:
1 | List((Symbol(a),5), (Symbol(b),2), (Symbol(c),2), (Symbol(d),1)) |
解释:
-
l2.toMap 将List((a,1), (b,1), (c,1)) 转换为immutable.Map(a->1, b->1, c->1) -
foldLeft 遍历列表#1l1 的每个元组。-
将 list1 的
(a,4) 添加到生成的映射中,结果为immutable.Map(a->1+4, b->1, c->1) -
将 list1 的
(b,1) 添加到生成的映射中,结果为immutable.Map(a->5, b->2, c->1) -
将 list1 的
(c,1) 添加到生成的映射中,结果为immutable.Map(a->5, b->2, c->2) -
将list1的
(d,1) 添加到生成的地图中,结果为immutable.Map(a->5, b->2, c->2, d->1)
-
将 list1 的
-
toList 将地图转换回原始输入形式,即List[(Symbol, Int)]
如果你可以假设
1 2 3 4 5 6 7 8 | 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) } |
不幸的是,这不是尾递归。