可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
From 2 lists of the form List[(Int, String)
:
l1 = List((1,"a"),(3,"b"))
l2 = List((3,"a"),(4,"c"))
how can I combine the Integer
s where the String
s are the same to get this third list:
l3 = List((4,"a"),(3,"b"),(4,"c"))
Right now I'm traversing both of the lists and adding if the strings are the same, but I think there should be a simple solution with pattern matching.
回答1:
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.
But I suppose there is a more elegant way to do the updated
part.
回答2:
How about,
(l1 ++ l2).groupBy(_._2).mapValues(_.unzip._1.sum).toList.map(_.swap)
Unpacking this a little on the REPL helps to show what's going on,
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))
回答3:
With Scalaz, this is a snap.
import scalaz._
import Scalaz._
val l3 = (l1.map(_.swap).toMap |+| l2.map(_.swap).toMap) toList
The |+|
method is exposed on all types T
for which there exists an implementation of Semigroup[T]
. And it just so happens that the semigroup for Map[String, Int]
is exactly what you want.
回答4:
for ( (k,v) <- (l1++l2).groupBy(_._2).toList ) yield ( v.map(_._1).sum, k )
回答5:
Note that with this solution, the lists are traversed twice.
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
}
回答6:
Another opaque onetwo-liner of questionable efficiency yet indubitable efficacy:
val lst = l1 ++ l2
lst.map(_._2).distinct.map(i => (lst.filter(_._2 == i).map(_._1).sum, i))
回答7:
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)
回答8:
An alternative to Miles Sabin's answer using Scala 2.13
's new groupMapReduce method which is (as its name suggests) an equivalent (more efficient) of a groupBy
followed by mapValues
and a reduce
step:
(l1 ::: l2).groupMapReduce(_._2)(_._1)(_ + _).toList.map(_.swap)
// List[(Int, String)] = List((3,b), (4,a), (4,c))
This:
prepends l1
to l2
group
s elements based on their second tuple part (group part of groupMapReduce)
map
s grouped values to their first tuple part (map part of groupMapReduce)
reduce
s values (_ + _
) by summing them (reduce part of groupMapReduce)
and finally swap
s tuples' parts.
This is an equivalent version performed in one pass (for the group/map/reduce part) through the List of:
(l1 ::: l2).groupBy(_._2).mapValues(_.map(_._1).reduce(_ + _)).toList.map(_.swap)