我写了以下代码来按键求和:
// ("A", 1) ("A", 4)
// ("B", 2) --> ("B", 2)
// ("A", 3)
def sumByKeys[A](tuples: List[(A, Long)]) : List[(A, Long)] = {
tuples.groupBy(_._1).mapValues(_.map(_._2).sum).toList
}
有更好的方法吗?
更新:在最后添加.toList
答案 0 :(得分:2)
我想这是最简单的不可变形式,而不使用scala之上的任何其他框架。
UPD 其实忘记了最终的toList。由于mapValues视图返回类型
,这在性能方面完全不同你可以尝试foldLeft,tailrec,一些可变的东西,它们有更好的性能
import annotation.tailrec
@tailrec
final def tailSum[A](tuples: List[(A, Long)], acc: Map[A, Long] = Map.empty[A, Long]): List[(A, Long)] = tuples match {
case (k, v) :: tail => tailSum(tail, acc + (k -> (v + acc.get(k).getOrElse(0L))))
case Nil => acc.toList
}
def foldLeftSum[A](tuples: List[(A, Long)]) = tuples.foldLeft(Map.empty[A, Long])({
case (acc, (k, v)) => acc + (k -> (v + acc.get(k).getOrElse(0L)))
}).toList
def mutableSum[A](tuples: List[(A, Long)]) = {
val m = scala.collection.mutable.Map.empty[A, Long].withDefault(_ => 0L)
for ((k, v) <- tuples) m += (k -> (v + m(k)))
m.toList
}
更新的性能测试在https://gist.github.com/baskakov/8437895,简要说明:
scala> avgTime("default", sumByKeys(tuples))
default avg time is 63 ms
scala> avgTime("tailrec", tailSum(tuples))
tailrec avg time is 48 ms
scala> avgTime("foldleft", foldLeftSum(tuples))
foldleft avg time is 45 ms
scala> avgTime("mutableSum", mutableSum(tuples))
mutableSum avg time is 41 ms
答案 1 :(得分:1)
我能想到的最好能让你稍微更好的表现并保存两个字符:
def sumByKeys[A](tuples: List[(A, Long)]) : List[(A, Long)] = {
tuples.groupBy(_._1).mapValues(_.unzip._2.sum)
}
在我的机器上使用Bask.ws的基准测试时,如果没有unzip
,则需要11毫秒而不是13毫秒。
编辑:事实上我认为性能必须相同......不知道那些2ms来自哪里
答案 2 :(得分:1)
与您的解决方案非常相似的解决方案:
def sumByKeys[A](tuples: List[(A, Long)]): List[(A, Long)] =
tuples groupBy (_._1) map { case (k, v) => (k, v.map(_._2).sum) } toList
val l: List[(String, Long)] = List(("A", 1), ("B", 2), ("A", 3))
sumByKeys(l)
// result:
// List[(String, Long)] = List((A,4), (B,2))
有趣的是,在您的解决方案中,您使用def mapValues[C](f: (B) ⇒ C): Map[A, C]
根据文档具有“懒惰”评估:“通过将函数应用于每个检索到的值来转换此地图。”
另一方面,def map[B](f: (A) ⇒ B): Map[B]
将构建一个新集合:“通过将函数应用于此不可变映射的所有元素来构建新集合。”
因此,根据您的需求,您可能懒得评估一张大地图,或急切地评估一张小地图。
答案 3 :(得分:0)
使用reduce,
def sumByKeys[A](tuples: List[(A, Long)]): List[(A, Long)] = {
tuples groupBy(_._1) map { _._2 reduce { (a,b) => (a._1, a._2+b._2) } } toList
}
的缩写
def sumByKeys[A](tuples: List[(A, Long)]): List[(A, Long)] = {
tuples groupBy(_._1) map { case(k,v) => v reduce { (a,b) => (a._1, a._2+b._2) } } toList
}