Scala - 在Map的Map上进行分组

3
我有一个变量是 Map[T, Map[Date, Double]],我想将其转换为 Map[Date, List[Double]],其中日期映射到原始数据结构中它映射的所有双精度数的列表。我很难找到正确的函数组合以获得所需的结构。
示例:
scala> val m1 = Map("2013-01-01" -> 10, "2014-01-01" -> 20)
scala> val m2 = Map("2013-01-01" -> 100, "2014-01-01" -> 200)
scala> val m = Map(1 -> m1, 2 -> m2)

操作 m 的期望结果:
Map("2013-01-01" -> List(10, 100), "2014-01-01" -> List(20, 200))
2个回答

4

没有可变集合:

m.values.
  flatMap{_.seq}.
  groupBy{_._1}.
  map{ case (k, vl) => k -> vl.map{_._2}.toList }
// Map(2014-01-01 -> List(20, 200), 2013-01-01 -> List(10, 100))

您可以使用 mapValues{ _.map{_._2}.toList } 代替 map{ case (k, vl) => k -> vl.map{_._2}.toList },但在这种情况下,每次访问值时都会重新评估。
使用 scalaz
import scalaz._, Scalaz._

m.values.toVector.foldMap{_.mapValues{List(_)}}

1
+1 给 Scalaz!Mike,如果你正在与 Brian 或 Chris 合作,我敢打赌他们会更喜欢这个答案。非常简短,非常简洁。 - wheaties
@wheaties:非常短,非常简洁,但非常低效。N个Map合并。 - senia
1
哈哈,嗯,当你使用Scalaz时,几乎总会付出性能代价。真正的问题是,这是否在性能关键区域,并且GC压力是否会对应用程序的其余部分造成破坏。也就是说,如果你受到I/O限制,那么谁在乎呢?如果你受到CPU限制,那就要注意了。 - wheaties
@wheaties 你能被 JIT 绑定吗? - ziggystar

1

你需要使用 ListBuffer 否则会出现覆盖键的风险:

val builder = MMap[Date, ListBuffer[Double]]
for{
  (_, inner) <- myNested
  (key, value) <- inner
} {
  if(builder contains key) builder(key) += value
  else builder(key) = new ListBuffer(value)
}

builder mapValues(_ result ()) toMap

其中MMap指的是可变映射。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接