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

时间:2012-08-28 19:36:51

标签: scala scala-collections

假设你有

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

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

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

效果不错,但我想知道如何以更“实用”的方式做到这一点,而不是诉诸可变地图?

3 个答案:

答案 0 :(得分: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

答案 1 :(得分: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)))}

答案 2 :(得分:0)

Scala 2.13开始,在flatten列出列表之后,我们可以使用groupMapReduce,它是groupBy / mapValues的单次通过选择:

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

此:

  • flatten s List s中的List作为List

  • group的列表元素(identity)( MapReduce的组部分)

  • map将每个分组值出现的次数设为1(_ => 1)(映射组 Map Reduce)

  • reduce在一组值(_ + _)中的值相加(减少groupMap Reduce 的一部分)。