alexkrishnan
alexkrishnan

Reputation: 1185

Fold from Map[String,List[Int]] to Map[String,Int]

I'm fairly new to Scala and functional approaches in general. I have a Map that looks something like this:

val myMap: Map[String, List[Int]]

I want to end up something that maps the key to the total of the associated list:

val totalsMap: Map[String, Int]

My initial hunch was to use a for comprehension:

val totalsMap = for (kvPair <- myMap) {
    kvPair._2.foldLeft(0)(_+_)
}

But I have no idea what I would put in the yield() clause in order to get a map out of the for comprehension.

Upvotes: 2

Views: 559

Answers (3)

ntn
ntn

Reputation: 1177

You can use mapValues for this,

val totalMap = myMap.mapValues(_.sum)

But mapValues will recalculate the sum every time you get a key from the Map. e.g. If you do totalMap("a") multiple times, it will recalculate the sum each time.

If you don't want this, you should use

val totalMap = myMap map { 
  case (k, v) => k -> v.sum
}

Upvotes: 5

Chris Albright
Chris Albright

Reputation: 154

val m = Map("hello" -> Seq(1, 1, 1, 1), "world" -> Seq(1, 1))
for ((k, v) <- m) yield (k, v.sum)

yields

Map(hello -> 4, world -> 2)`

The for comprehension will return whatever monadic type you give it. In this case, m is a Map, so that's what's going to come out. The yield must return a tuple. The first element (which becomes the key in each Map entry) is the word you're counting, and the second element (you guessed it, the value in each Map entry) becomes the sum of the original sequence of counts.

Upvotes: 1

Michael Zajac
Michael Zajac

Reputation: 55569

mapValues would be more suited for this case:

val m = Map[String, List[Int]]("a" -> List(1,2,3), "b" -> List(4,5,6))

m.mapValues(_.foldLeft(0)(_+_))

res1: scala.collection.immutable.Map[String,Int] = Map(a -> 6, b -> 15)

Or without foldLeft:

m.mapValues(_.sum)

Upvotes: 2

Related Questions