Yan*_*san 1 scala scala-collections
我需要一个按元组列表分组和平均的方法.
这是我目前的实施:
// (a, 1), (a, 2), (b, 3) -> (a, 1.5), (b, 3)
def groupByAndAvg[T, U](ts: Iterable[(T, U)])(implicit num: Numeric[U]): Iterable[(T, Double)] = {
ts.groupBy(_._1)
.mapValues { _.unzip._2 }
.mapValues { xs => num.toDouble(xs.sum) / xs.size }
}
Run Code Online (Sandbox Code Playgroud)
有没有一个更好的方式在性能或简单性?
unzip当你只需要它的一半结果时,没有理由生成两个集合.只是做就.map(_._2)应该得到你所需要的.map而不是mapValues因为mapValues只创建新映射的视图,这意味着将在每次访问时重新计算平均值.Iterable因为那时你失去了groupBy给你一个的事实Map.所以也许你想要这个:
def groupByAndAvg[T, U](ts: Iterable[(T, U)])(implicit num: Numeric[U]) = {
ts.groupBy(_._1).map {
case (key, pairs) =>
val values = pairs.map(_._2)
key -> (num.toDouble(values.sum) / values.size)
}
}
groupByAndAvg(Vector(("a", 1), ("a", 2), ("b", 3)))
// res0: scala.collection.immutable.Map[String,Double] = Map(b -> 3.0, a -> 1.5)
Run Code Online (Sandbox Code Playgroud)
如果你经常做这样的事情,你可以定义自己的收集方法.在这里我定义groupByKey,它取a Traverable[(K,V)]并返回a Map[K,Traverable[V]],和avg:
import scala.collection.TraversableLike
import scala.collection.generic.CanBuildFrom
import scala.collection.immutable
import scala.collection.mutable
implicit class EnrichedWithGroupByKey[K, V, Repr](val self: TraversableLike[(K, V), Repr]) extends AnyVal {
def groupByKey[That](implicit bf: CanBuildFrom[Repr, V, That]): Map[K, That] = {
val m = mutable.Map.empty[K, mutable.Builder[V, That]]
for ((key, value) <- self) {
val bldr = m.getOrElseUpdate(key, bf(self.asInstanceOf[Repr]))
bldr += value
}
val b = immutable.Map.newBuilder[K, That]
for ((k, v) <- m)
b += (k -> v.result)
b.result
}
}
implicit class EnrichedWithAvg[A](val self: Traversable[A])(implicit num: Numeric[A]) {
def avg = {
assert(self.nonEmpty, "cannot average an empty collection")
num.toDouble(self.sum) / self.size
}
}
Run Code Online (Sandbox Code Playgroud)
然后你可以这样做:
def groupByAndAvg[T, U](ts: Iterable[(T, U)])(implicit num: Numeric[U]) = {
ts.groupByKey.map{ case (k,vs) => k -> vs.avg }
}
groupByAndAvg(Vector(("a", 1), ("a", 2), ("b", 3)))
// res0: scala.collection.immutable.Map[String,Double] = Map(b -> 3.0, a -> 1.5)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2337 次 |
| 最近记录: |