zenofsahil
zenofsahil

Reputation: 1753

Convert scala array to Map of item counts

I'm looking for a neat way of converting a scala array to a map containing the frequency of the items occurring in the array.

For eg. :

Given an array like:

val arr = Array("one", "one", "two", "three", "one", "three")

I want a map:

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

I can do this by writing a function such as

import scala.collection.mutable

def counter[T](arr: Array[T]) = {
  val temp = mutable.Map[T, Int]()
  for (i <- arr) {
    if (temp.contains(i)) temp(i) += 1
    else temp(i) = 1
  }
  temp
}

counter(arr)

I'd like to know if this can be done more efficiently.

Upvotes: 1

Views: 5145

Answers (2)

Martin Milichovsky
Martin Milichovsky

Reputation: 740

I'd use groupBy(identity) and mapValues(_.length):

scala> val arr = Array("one", "one", "two", "three", "one", "three")
arr: Array[String] = Array(one, one, two, three, one, three)

scala> arr.groupBy(identity).mapValues(_.length)
res0: scala.collection.immutable.Map[String,Int] = Map(one -> 3, three -> 2, two -> 1)

Update: This isn't more efficient than your code (and I think it's not possible to beat), but it is definitely more readable. The groupBy method aggregates all the identical (because we used identity) values to one Array, that has some overhead compared to just incrementing count.

Update 2: As noted in the comment, the output is not serializable because the mapValues just wraps the map and evaluates the function on each get. You can materialize the map by map(identity) on the wrapped map:

arr.groupBy(identity).mapValues(_.length).map(identity)

but it's not very nice code.

Upvotes: 6

Samar
Samar

Reputation: 2101

arr.groupBy(identity).map{case (x,y) => x -> y.size} 

Upvotes: 6

Related Questions