如何合并两个元组列表?

11

我有两个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))

11
看起来你正在参加“Scala函数式编程原理”课程... - michael.kebe
@michael.kebe 看起来他们每年不改变任务分配 :( - Artem Oboturov
1
可能是Scala中两个列表的合并的重复问题。 - Suma
4个回答

21
您可以尝试以下一行代码:
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))

其中l1l2是您想要合并的元组列表。

现在,这个过程:

  • (l1 ++ l2) 您只需连接两个列表即可
  • .groupBy( _._1) 您将所有元组按其第一个元素分组。您将收到一个Map,其第一个元素作为键,以此元素开头的元组列表作为值。
  • .map( kv => (kv._1, kv._2.map( _._2).sum ) ) 您创建一个新映射,具有类似的键,但值是所有第���个元素的总和。
  • .toList 将结果转换回列表。

或者,您可以使用模式匹配来访问元组元素。

( l1 ++ l2 ).groupBy( _._1 ).map{
  case (key,tuples) => (key, tuples.map( _._2).sum ) 
}.toList

4

或者你也可以使用mapValues来缩短代码。

mapValues,顾名思义,允许重新映射由groupBy创建的Map中每个(键、值)对的值。

在这种情况下,传递给mapValues的函数将每个(Char,Int)元组减少到仅为Int,然后对所得到的Int列表求和。

(l1 ::: l2).groupBy(_._1).mapValues(_.map(_._2).sum).toList

如果输出列表的顺序需要遵循你的例子,只需添加sorted,它依赖于一个(Ordering[(Char, Int)])的隐式实例。
(l1 ::: l2).groupBy(_._1).mapValues(_.map(_._2).sum).toList.sorted

0
如果你可以假设两个 List[(A,B)] 都按照 Ordering[A] 排序,那么你可以写出类似这样的代码:
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)
}

不幸的是,这不是尾递归。


0

使用 foldLefttoMap:

从一个列表中获取一个映射,并遍历第二个列表。我们将条目插入到映射中。

l1.foldLeft(l2.toMap)((accumulator, tuple) =>
      accumulator + (tuple._1 -> (accumulator.getOrElse(tuple._1, 0) + tuple._2))
).toList

转换为:

List((Symbol(a),5), (Symbol(b),2), (Symbol(c),2), (Symbol(d),1))

解释:

  • l2.toMapList((a,1), (b,1), (c,1)) 转换为 immutable.Map(a->1, b->1, c->1)
  • foldLeft 遍历列表 #1 l1 中的每个元组。
    • 将列表1中的 (a,4) 添加到生成的映射中,结果为 immutable.Map(a->1+4, b->1, c->1)
    • 将列表1中的 (b,1) 添加到生成的映射中,结果为 immutable.Map(a->5, b->2, c->1)
    • 将列表1中的 (c,1) 添加到生成的映射中,结果为 immutable.Map(a->5, b->2, c->2)
    • 将列表1中的 (d,1) 添加到生成的映射中,结果为 immutable.Map(a->5, b->2, c->2, d->1)
  • toList 将映射转换回原始输入形式,即 List[(Symbol, Int)]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接