计算Scala中List [List [T]]中每个元素的出现次数

Mir*_* N. 9 scala scala-collections

假设你有

val docs = List(List("one", "two"), List("two", "three"))
Run Code Online (Sandbox Code Playgroud)

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

Map("one" -> 1, "two" -> 2, "three" -> 1)
Run Code Online (Sandbox Code Playgroud)

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

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

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

它运作得很好,但我想知道如何以更"功能"的方式做到这一点,而不是诉诸一个可变的地图?

dhg*_*dhg 20

试试这个:

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

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

docs.flatten.groupBy(identity).map(x => (x._1, x._2.size))
Run Code Online (Sandbox Code Playgroud)

这个identity功能只是意味着x => x.

  • @Val"毫无疑问"?好吧,我刚刚检查了`l.groupBy(identity).mapValues(_.size)`对`l.foldLeft(Map.empty [Int,Int] .withDefaultValue(0))((m,x)=> m +( x - >(1 + m(x))))`其中`l`是`(1到10000).map(_ => scala.util.Random.nextInt(100)).toList`.通过5000次试验,"groupBy"方法花费了2510 ms,而"foldLeft"方法花费了8349 ms.我用许多其他发行版和不同的机器重复了这个实验.无论如何,如果你真的看看`groupBy`的实现,你会明白为什么:) (4认同)

Lan*_*dei 13

docs.flatten.foldLeft(new Map.WithDefault(Map[String,Int](),Function.const(0))){
  (m,x) => m + (x -> (1 + m(x)))}
Run Code Online (Sandbox Code Playgroud)

什么火车残骸!

[编辑]

啊,那更好!

docs.flatten.foldLeft(Map[String,Int]() withDefaultValue 0){
  (m,x) => m + (x -> (1 + m(x)))}
Run Code Online (Sandbox Code Playgroud)

  • 你可以缩短地图初始化:`docs.flatten.foldLeft(Map [String,Int]()withDefaultValue 0){(m,x)=> ...}` (2认同)