9

假设你有

val docs = List(List("one", "two"), List("two", "three"))

其中,例如 List("one", "two") 表示包含术语“one”和“two”的文档,并且您希望构建一个包含每个术语的文档频率的地图,即在这种情况下

Map("one" -> 1, "two" -> 2, "three" -> 1)

你会如何在 Scala 中做到这一点?(并且以一种有效的方式,假设一个更大的数据集。)

我的第一个类似 Java 的想法是使用可变映射:

val freqs = mutable.Map.empty[String,Int]
for (doc <- docs)
  for (term <- doc)
    freqs(term) = freqs.getOrElse(term, 0) + 1

这工作得很好,但我想知道你如何以更“实用”的方式做到这一点,而不诉诸可变地图?

4

3 回答 3

20

试试这个:

scala> docs.flatten.groupBy(identity).mapValues(_.size)
res0: Map[String,Int] = Map(one -> 1, two -> 2, three -> 1)

如果您要多次访问计数,那么您应该避免,mapValues因为它是“懒惰的”,因此会在每次访问时重新计算大小。此版本为您提供相同的结果,但不需要重新计算:

docs.flatten.groupBy(identity).map(x => (x._1, x._2.size))

identity功能仅表示x => x.

于 2012-08-28T19:37:59.603 回答
13
docs.flatten.foldLeft(new Map.WithDefault(Map[String,Int](),Function.const(0))){
  (m,x) => m + (x -> (1 + m(x)))}

什么火车残骸!

[编辑]

啊,这样更好!

docs.flatten.foldLeft(Map[String,Int]() withDefaultValue 0){
  (m,x) => m + (x -> (1 + m(x)))}
于 2012-08-28T21:19:24.737 回答
0

开始Scala 2.13,在flattening 列表列表之后,我们可以使用which 是/groupMapReduce的一次性替代方案:groupBymapValues

// val docs = List(List("one", "two"), List("two", "three"))
docs.flatten.groupMapReduce(identity)(_ => 1)(_ + _)
// Map[String,Int] = Map("one" -> 1, "three" -> 1, "two" -> 2)

这:

  • flattens 的ListsList作为List

  • groups 列表元素 ( )(MapReduceidentity的组部分)

  • maps 每个分组值出现为 1 ( _ => 1)(映射组Map Reduce 的一部分)

  • reduces 值在一组值 ( _ + _) 中,通过对它们求和(减少 groupMap Reduce的一部分)。

于 2019-03-10T21:25:23.920 回答