Categories
Tech

Scala Saturday – The map Method

One of the most frequently used operations in functional programming is the map operation. The map operation takes a collection of values, performs some transformation on each element, and creates a new collection containing those new elements. The elements in the new collection can be the same type as the elements in the original collection, or they may be of a different type. It just depends on the transformation.

Sequential Collections

Let’s look at a couple of examples. First, say that you have a list of integers, and you need the squares of those integers:

Illustrates mapping a squaring function over a list of integers (2, 4, 6, 8) to produce a new list of integers (4, 16, 36, 64)
Mapping a Squaring Function over a List of Integers

You can define the function f to calculate the square of a number. Then you can call the map method to apply f to each member in the input list and create a new list containing the squares of the items in the original list. In functional programming parlance, we say that you map f over the list to produce the new list. The code looks like this:

val f = (n: Int) => n * n
val squares = List(2,4,6,8) map f
// squares: List[Int] = List(4, 16, 36, 64)

Of course, you can also define the mapping function inline:

val squares = List(2,4,6,8) map { n => n * n }
// squares: List[Int] = List(4, 16, 36, 64)

Second, suppose you need to compute the length of each string in a list:

Illustrates mapping a function to calculate string length over a list of strings ("five", "six", "pick up", "sticks") to produce a list of integers (4, 3, 7, 6)
Mapping a Length Function over a List of Strings

Similar to the last example, just define a function to calculate the length of a string, and map it over the list of strings with the map method:

val lengths =
  List("five", "six", "pick up", "sticks") map {
    _.length
  }
// lengths: List[Int] = List(4, 3, 7, 6)

Notice two things:

  1. In both examples, the output list maintains the order of the elements in the input list. This happens because a list is a sequential collection. The same is also true for an array and a sequence. In contrast, as we will see in a moment, order is not maintained when mapping over a non-sequential collection like a set.
  2. Whereas the type of both the input list and the output list is the same in the first example—they both contain integers—in the second example, the type of the elements in the output list (integers) is different from the type of the elements of the input list (strings). This happens frequently in functional programming. It is not uncommon to move a collection of inputs through a series of transformations, changing the type with each transformation.

Sets

The Set class(es) also defines a map method. It behaves essentially the same as the map methods for lists, arrays, or sequences, but there are some minor differences. Sets are not sequential collections, so the order of their elements is not guaranteed. In other words, if you iterate through the input set and get the elements out in a certain order, after you map over it to get the output set, iterating over the elements of the output set may not yield the same order as the counterpart elements in the input set. Furthermore, by definition, sets cannot contain duplicates. So then, what happens if you map a function over the input set that produces duplicate output values? Let’s see:

val lengths = Set("four", "five", "six", "seven") map {
  _.length
}
// lengths: scala.collection.immutable.Set[Int] = Set(4, 3, 5)

That makes sense. Duplicates are dropped. Well, now you know. And knowing is half the battle. (What’s the other half, though? They never did tell us that!)

Strings

Finally, you can map over strings as well. Mapping over strings operates on each character in the string:

val allcaps = "sesquipedalian" map { _.toUpper }
// allcaps: String = SESQUIPEDALIAN

If you need to transform the characters into values of another type, map can take a function that transforms characters into a different type:

val allcaps = "sesquipedalian".toSeq map { _.toInt }
// allcaps: Seq[Int] = Vector(115, 101, 115, ... )

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.