在Scala中组合两个列表

时间:2011-10-01 17:08:26

标签: list scala pattern-matching

来自List[(Int, String)形式的2个列表:

l1 = List((1,"a"),(3,"b"))
l2 = List((3,"a"),(4,"c"))

如何组合Integer s所在的String s以获得第三个列表:

l3 = List((4,"a"),(3,"b"),(4,"c"))

现在我正在遍历两个列表并添加字符串是否相同,但我认为应该有一个简单的模式匹配解决方案。

8 个答案:

答案 0 :(得分:20)

val l = l1 ::: l2
val m = Map[String, Int]()
(m /: l) {
  case (map, (i, s)) => { map.updated(s, i + (map.get(s) getOrElse 0))}
}.toList // Note: Tuples are reversed.

但我认为有更优雅的方式来完成updated部分。

答案 1 :(得分:20)

怎么样,

(l1 ++ l2).groupBy(_._2).mapValues(_.unzip._1.sum).toList.map(_.swap)

在REPL上解压缩一点有助于显示正在发生的事情,

scala> l1 ++ l2
res0: List[(Int, java.lang.String)] = List((1,a), (3,b), (3,a), (4,c))

scala> res0.groupBy(_._2)
res1: ... = Map(c -> List((4,c)), a -> List((1,a), (3,a)), b -> List((3,b)))

scala> res1.mapValues(_.unzip)
res2: ... = Map(c -> (List(4),List(c)), a -> (List(1, 3),List(a, a)), b -> (List(3),List(b)))                         

scala> res1.mapValues(_.unzip._1)                                                                                                                                                                      
res3: ... = Map(c -> List(4), a -> List(1, 3), b -> List(3))                                                                                    

scala> res1.mapValues(_.unzip._1.sum)
res4: ... = Map(c -> 4, a -> 4, b -> 3)                                                                                                               

scala> res4.toList                                                                                                                                                                                     
res5: List[(java.lang.String, Int)] = List((c,4), (a,4), (b,3))                                                                                                                                        

scala> res5.map(_.swap)
res6: List[(Int, java.lang.String)] = List((4,c), (4,a), (3,b))

答案 2 :(得分:10)

使用Scalaz,这很容易。

import scalaz._
import Scalaz._

val l3 = (l1.map(_.swap).toMap |+| l2.map(_.swap).toMap) toList

|+|方法在所有类型T上公开,其中存在Semigroup[T]的实现。恰好,Map[String, Int]的半群恰好是你想要的。

答案 3 :(得分:1)

for ( (k,v) <- (l1++l2).groupBy(_._2).toList ) yield ( v.map(_._1).sum, k )

答案 4 :(得分:0)

请注意,使用此解决方案,列表将遍历两次。

val l3 = (l1 zip l2).foldRight(List[(Int, String)]()) {
  case ((firstPair @ (firstNumber, firstWord),
        secondPair @ (secondNumber, secondWord)),
        result) =>
    if (firstWord == secondWord)
      ((firstNumber + secondNumber), firstWord) :: result
    else
      firstPair :: secondPair :: result
}

答案 5 :(得分:0)

另一个不透明的一个两行,效率可疑但不容置疑:

val lst = l1 ++ l2
lst.map(_._2).distinct.map(i => (lst.filter(_._2 == i).map(_._1).sum, i))

答案 6 :(得分:0)

val a = List(1,1,1,0,0,2)
val b = List(1,0,3,2)

scala> List.concat(a,b)
res31: List[Int] = List(1, 1, 1, 0, 0, 2, 1, 0, 3, 2)

(or) 

scala> a.:::(b)
res32: List[Int] = List(1, 0, 3, 2, 1, 1, 1, 0, 0, 2)

(or) 

scala> a ::: b
res28: List[Int] = List(1, 1, 1, 0, 0, 2, 1, 0, 3, 2)

答案 7 :(得分:0)

使用truffle migrate的新Miles Sabin's answer方法替代groupMapReduce的方法,顾名思义,等效于Scala 2.13,后跟{ {1}}和一个groupBy步骤:

mapValues

此:

  • reduce之前(l1 ::: l2).groupMapReduce(_._2)(_._1)(_ + _).toList.map(_.swap) // List[(Int, String)] = List((3,b), (4,a), (4,c))

  • l1的元素基于它们的第二元组部分( MapReduce的组部分)

  • l2的值分组到其第一个元组部分(组 Map Reduce的映射部分)

  • group的值(map)通过求和(减少groupMap Reduce 的一部分)

  • ,最后是reduce的元组部分。

这是通过以下列表的等效版本performed in one pass(对于组/地图/归约部分):

_ + _