Scala: Calling higher-order functions on Maps


As I’m learning Scala, I played with Maps. The subtlety of Maps has to do with the fact that they are tuples of (key,value) pairs. Let’s look at the various ways of calling higher-order functions on them.

Let’s consider a map of bears and they weights created as follows.

scala> val myMap = Map("Brown Bear" -> 635, "Grizzly Bear" -> 360, "American Black Bear" -> 270, "Polar Bear" -> 680)
myMap: scala.collection.immutable.Map[String,Int] = Map(Brown Bear -> 635, Grizzly Bear -> 360, American Black Bear -> 270, Polar Bear -> 680)

Filtering bears that weigh more than 300kg

If I want to filter bears that weigh more than 300kg, I’ll use the filter function. I can do it by passing in a function (x) => x._2 > 300.

scala> myMap.filter((x) => x._2 > 300)
res0: scala.collection.immutable.Map[String,Int] = Map(Brown Bear -> 635, Grizzly Bear -> 360, Polar Bear -> 680)

I can also use a wildcard instead of defining defining the parameter x.

scala> myMap.filter(_._2 > 300)
res0: scala.collection.immutable.Map[String,Int] = Map(Brown Bear -> 635, Grizzly Bear -> 360, Polar Bear -> 680)

A third way would be to use a case in order have the tuple split for us into its two parts as input to the function.

scala> myMap.filter {case (x,y) => y>300}
res2: scala.collection.immutable.Map[String,Int] = Map(Brown Bear -> 635, Grizzly Bear -> 360, Polar Bear -> 680)

As I don’t car about the first part of the tuple, I can just use a wildcard instead of (x,y).

scala> myMap.filter {case (_,y) => y>300}
res12: scala.collection.immutable.Map[String,Int] = Map(Brown Bear -> 635, Grizzly Bear -> 360, Polar Bear -> 680)

Averaging the weights of the bears

Let’s say that I want to average the weight of the various bears. To do so, I have to use foldLeft on the map and then divide the outcome by the size of the map.

scala> myMap.foldLeft (0) ((sum,v) => sum+v._2) / myMap.size
res5: Int = 486

Instead of using defining the signature of the anonymous function, I can also use wildcards.

scala> myMap.foldLeft (0) (_+_._2) / myMap.size
res7: Int = 486

Now I can even use the scala shorthand for foldLeft even though I’m no real fan of it.

scala> (0/:myMap) (_+_._2) / myMap.size
res9: Int = 486

The shorthand is equivalent to the following as methods that end with a colon associate to the right.

scala> (myMap./:(0)) (_+_._2) / myMap.size
res10: Int = 486

Actually, the easiest way can also be to directly work on the values and call the sum function on those values.

scala> myMap.values.sum / myMap.size
res6: Int = 486

Averaging the weights of the bears that weigh more than 300kg

If I now want to average the weights of the bears that weight more than 300kg, I can first filter the weights that are greater than 300kg. I can  do that by filtering a Map and returning a Map using filter. I can also filter the values by first calling values

Let’s first calculate the average on the filtered list of values.

scala> myMap.values.filter(_>300).sum / myMap.size
res18: Int = 418

Let’s now do it by always working on a Map and using FoldLeft.

scala> myMap.filter(_._2>300).foldLeft(0)(_+_._2) / myMap.size
res0: Int = 418

Final thoughts

With Scala, there are so many ways to do the same thing that in the end, it’s just a matter or taste. Nevertheless, when working in a team, it’s good practice to try to keep the style uniform throughout the code base.

This post did not aim at listing all the possible ways to implement the examples. Given the nature of Scala, I even imagine that there are more clever ways to do it.

A last consideration has to do with performance. Given the size of my example, all solutions run pretty fast. This shall no doubt be different with larger collections.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: