2

I'm new to Scala.

If I have the following List:

val ls = List("a", "a", "a", "b", "b", "c")

how can I create a Map that holds an number of appearances for every element in the list?

For example the Map for the list above should be:

Map("a" -> 3, "b" -> 2, "c" -> 1)
Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
danny.lesnik
  • 18,479
  • 29
  • 135
  • 200
  • See http://stackoverflow.com/questions/11448685/scala-how-can-i-count-the-number-of-occurrences-in-a-list - search for "count frequency" or "count occurrence". – user2246674 Sep 13 '13 at 19:52
  • the solutions there seem iterate over the collection multiple times, and don't yield a `Map`; in fact, they yield, imho, quite a useless result, in addition to being inefficient. – Erik Kaplun Sep 13 '13 at 20:16

3 Answers3

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

snippet in action:

scala> val list = List("a", "a", "b", "c", "c", "a")
list: List[String] = List(a, a, b, c, c, a)

scala> list.foldLeft(Map[String, Int]() withDefaultValue 0) { (m, x) => m + (x -> (1 + m(x))) }
res1: scala.collection.immutable.Map[String,Int] = Map(a -> 3, b -> 1, c -> 2)

(directly based on Count occurrences of each element in a List[List[T]] in Scala)

Community
  • 1
  • 1
Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
  • Wow, Can you please elaborate on this example? if "Map[String, Int]() withDefaultValue 0" means that the Empty Map will be our Initial Value in foldLeft, what would be the meaning of { (m, x) => m + (x -> (m(x) + 1)) }? – danny.lesnik Sep 13 '13 at 20:52
  • Assuming you know how `foldLeft` works in general, the expression you're asking about is taking taking the accumulated map and adding or updating a key in it; it's an immutable `Map` so you don't update it but you create a new one by adding key-value pairs to it. Anyway, just look into `foldLeft`—it's a very common "pattern" in functional programming :) – Erik Kaplun Sep 14 '13 at 15:45
  • ...oh and it works because of the `withDefaultValue 0` part so you don't have to check if a value has already been seen before because the `Map` just returns `0` by default if the key doesn't exist; e.g. `(Map[String, Int]() withDefaultValue 0)("foo")` gives `0` instead of throwing an exception. – Erik Kaplun Sep 14 '13 at 15:50
2

Not as efficient as Erik's foldLeft solution:

val ls = List("a", "a", "a", "b", "b", "c")
ls.groupBy(identity).mapValues(_.size)
res0: scala.collection.immutable.Map[String,Int] = Map(a -> 3, c -> 1, b -> 2)
theon
  • 14,170
  • 5
  • 51
  • 74
2

With scalaz,

xs foldMap (x => Map(x -> 1))
Ben James
  • 121,135
  • 26
  • 193
  • 155
  • Nice to know; but gotta be a bit more into funprog than average to appreciate or perhaps even understand this one :) – Erik Kaplun Sep 14 '13 at 15:47