2012-08-28 13 views
8

Załóżmy, żezliczają każdego elementu na liście [Lista [t]] w Scala

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

gdzie np Lista („jeden”, „dwa”) przedstawia dokument zawierający warunki „jeden” i „dwa” i chcesz zbudować mapę z częstotliwością dokumentu dla każdego terminu, czyli w tym przypadku

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

Jak czy zrobiłbyś to w Scali? (I w sposób efektywny, przy założeniu znacznie większy zbiór danych).

Moja pierwsza myśl Java-jak jest użycie zmienny mapę:

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

który działa dość dobrze, ale zastanawiam się, jak można zrobić to w bardziej "funkcjonalny" sposób, bez uciekania się do mapy zmiennej?

Odpowiedz

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

Co za wrak pociągu!

[Edytuj]

Ah, to lepiej!

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

Możesz skrócić inicjalizację mapy:' docs.flatten.foldLeft (Map [String, Int]() withDefaultValue 0) {(m, x) => ...} ' – paradigmatic

+0

Dziękujemy!Nie mam pojęcia, dlaczego przeoczyłem tę funkcję ... – Landei

+1

Wydaje się, że jest szybszy niż "groupBy", więc oznaczam to jako zaakceptowane. Ale obie odpowiedzi są interesujące. –

18

Spróbuj tego:

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

Jeśli będzie dostępu do zliczenia wiele razy, to należy unikać mapValues ponieważ jest „leniwy”, a tym samym byłoby przeliczyć rozmiar na każdym dostępu. Wersja ta daje taki sam wynik, ale nie będzie wymagało recomputations:

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

Funkcja identity tylko oznacza x => x.

+0

Nice. Wydaje się jednak wolniejsze niż w przypadku map zmiennych, z zaledwie ~ 10k terminami. Koszt przekształcania zbiorów 3 razy? –

+0

Tak, jest ładny i funkcjonalny, ale kopiowanie wszystkich danych nie pomaga w wydajności. Zmienna wersja Map nie traci dużo czasu. – dhg

+0

+1 za nauczenie mnie, że 'mapValues' przelicza mapę przy każdym przemierzeniu. Ale w takim przypadku wyrażenie z 'foldLeft' powinno działać lepiej niż' groubBy'. – paradigmatic

Powiązane problemy