In a previous Scala Saturday post, we looked at the map
method. As a quick review, map
lets you take a collection of elements, calculate a new value from each source element, and create a new collection containing those new values. The mapping function has the following signature:
A => B
In other words, the function takes a single value of type A
and returns a single value of type B
.
But what if you have a mapping function with this signature?
A => List[B]
In other words, this function does not take one value and produce one value. Rather it takes one value and produces multiple values. What if you’re OK with that, but you just need to take those many values returned from each mapping and smash them all together into one list?
From Side to Side
Most of the guys in legendary progressive rock band Yes didn’t have just one job on their Close to the Edge album. They performed multiple duties. So let’s create a Musician
case class to represent a musician and the list of instruments he plays on the album:
case class Musician( name: String, instruments: List[String])
Now you can create a list of musicians:
val yes = List( Musician("Jon Anderson", List("vocals")), Musician( "Steve Howe", List("electric guitars", "acoustic guitars", "vocals") ), Musician( "Chris Squire", List("bass guitar", "acoustic guitars", "vocals") ), Musician( "Rick Wakeman", List("Hammond organ", "Minimoog", "Mellotron", "grand piano", "RMI 368 Electra-Piano and Harpsichord", "pipe organ at St. Giles, Cripplegate church") ), Musician( "Bill Bruford", List("drums", "percussion") ) )
Maybe you want one long list of every instrument the fellows in Yes used on the album. You just have to map each musician to his list of instruments, and then concatenate those lists together:
You could do this with two functions you’ve read about already on Scala Saturdays: map
and concat
:
val instruments = yes.map(_.instruments).flatten // instruments: List[String] = // List(vocals, electric guitars, acoustic guitars, // vocals, bass guitar, ...)
The Total Mass
What if there were a function that could do both of those things—perform the transformation and the concatenation all in one? Turns out that there is: flatMap
!
val instruments = yes flatMap (_.instruments) // instruments: List[String] = // List(vocals, electric guitars, acoustic guitars, // vocals, bass guitar, ...)
How ’bout that? The very same results all in one function!
So then, flatMap
performs a map and a flatten all in one function call.
You’ve seen flatMap
examples here only in terms of lists, but the Scala’s other collections define the flatMap
method, too: