如何按键排列元组列表

时间:2014-01-15 13:33:30

标签: scala scala-collections

我写了以下代码来按键求和:

  // ("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

4 个答案:

答案 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
}