Scala 如何按键对元组列表求和
我编写了以下代码以按键求和:Scala 如何按键对元组列表求和,scala,scala-collections,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我想这是最简单的不可变
// ("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
我想这是最简单的不可变形式,不需要在scala上使用任何额外的框架
UPD实际上忘记了最后的收费表。由于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
}
我所能想到的最好方法可以让您的性能稍好一点,并节省两个字符:
def sumByKeys[A](tuples: List[(A, Long)]) : List[(A, Long)] = {
tuples.groupBy(_._1).mapValues(_.unzip._2.sum)
}
在我的带有Bask.ws基准测试的机器上,没有解压
,需要11毫秒而不是13毫秒
编辑:事实上,我认为性能必须相同。。。不知道这些2ms来自何处与您的解决方案非常相似:
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))
有趣的是,在您的解决方案中,您使用了defmapvalues[C](f:(B)⇒ C) :Map[A,C]
根据文档,它有“惰性”评估:“通过对每个检索到的值应用函数来转换此映射。”
另一方面defmap[B](f:(A)⇒ B) :Map[B]
将构建新集合:“通过将函数应用于此不可变映射的所有元素来构建新集合。”
因此,根据您的需要,您可以懒洋洋地评估大地图,也可以急切地评估小地图。使用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
}
我认为这是最好的,我不知道
sum
方法,可能会使用foldLeft
来代替。这是Scalaz中Map
半群的行为,因此它允许您编写tuples.Map(Map()).suml.toList
,这并不一定更好。还要注意的是,您似乎在末尾缺少了一个toList
。还要注意的是,您可以使用foldMap
,而不是map
+suml
:tuples.foldMap{map()}.toList
,但是这两种scalaz
方法都非常低效-您将得到N
Map
合并。还要注意的是mapValues
返回一个视图,该视图将在每个get
上再次应用转换。如果没有sumByKeys
上的toList
,您只会得到mapValues
,这意味着您没有完成其他实现所做的大量工作。哇。你刚刚睁开了我的眼睛!我猜这是因为地图价值观?这使得完全不同的图片mapValues
很奇怪,它实际上返回了一个视图,但在返回类型中没有指出。11毫秒太小了。最好至少增加5次基准参数(平均循环计数和元组计数)。这将提供更多恒定和可区分的值。不要因为mapValues而忘记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
}