2012-08-28 19 views
8

Suponga que tieneContar apariciones de cada elemento en una lista [Lista [T]] en Scala

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

donde, por ejemplo, Lista ("uno", "dos") representa un documento que contenga los términos "uno" y "dos", y que desea construir un mapa con la frecuencia de documentos para cada término, es decir, en este caso

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

Cómo ¿Harías eso en Scala? (Y de una manera eficiente, asumiendo un conjunto de datos mucho más grande.)

Mi primer pensamiento similar a Java es utilizar un mapa mutable:

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

que funciona bastante bien, pero me pregunto cómo se podría hacer eso de una manera más "funcional", sin recurrir a un mapa mutable?

Respuesta

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

¡Qué desastre en el tren!

[Editar]

¡Ah, eso es mejor!

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

Puedes acortar la inicialización del mapa:' docs.flatten.foldLeft (Map [String, Int]() with Default value 0) {(m, x) => ...} ' – paradigmatic

+0

¡Gracias!No tengo idea de por qué he pasado por alto esa función ... – Landei

+1

Parece más rápido que 'groupBy', por lo que marcó esto como aceptado. Pero ambas respuestas son interesantes. –

18

Prueba esto:

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

Si va a ser el acceso a las cuentas muchas veces, a continuación, se debe evitar mapValues ya que es "flojo" y, por lo tanto, sería volver a calcular el tamaño de cada acceso. Esta versión le da el mismo resultado, pero no requerirá los nuevos cálculos:

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

La función identity sólo significa x => x.

+0

Nice. Sin embargo, parece más lento que con un mapa mutable, con solo ~ 10k términos. El costo de transformar colecciones 3 veces? –

+0

Sí, es agradable y funcional, pero copiar todos esos datos no está ayudando a la eficiencia. La versión Map mutable no pierde mucho tiempo. – dhg

+0

+1 por enseñarme que 'mapValues' recalcula el mapa en cada recorrido. Pero en ese caso, una expresión con 'foldLeft' debería funcionar mejor que' groubBy'. – paradigmatic

Cuestiones relacionadas