Подсчет вхождений каждого элемента в списке[List[T]] в Scala

Предположим, у вас есть

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

, который работает достаточно хорошо, но мне интересно, как вы может ли это сделать более "функциональным" способом, не прибегая к изменяемой карте?

2 ответов


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

что за крушение поезда!

[Edit]

вот так лучше!

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

попробуйте это:

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))

на значит x => x.