In a previous F# Friday post, we looked at the map
function. 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:
'T -> 'U
In other words, the function takes a single value of type 'T
and returns a single value of type 'U
.
But what if you have a mapping function with this signature?
'T -> 'U list
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?
The Universe Divided
The guys in Canadian power trio Rush didn’t just stick to one instrument on their Hemispheres album. Each of them played a collection of instruments. So let’s create a Musician
record type to represent a musician and the list of instruments he plays on the album:
type Musician = { Name : string Instruments : string list }
Now you can create a list of musicians:
let rush = [ { Name = "Geddy Lee"; Instruments = [ "vocals"; "bass guitar"; "Oberheim polyphonic"; "Minimoog"; "Moog Taurus pedals" ] }; { Name = "Alex Lifeson"; Instruments = [ "electric and acoustic guitars"; "classical guitar"; "guitar synthesizer"; "Moog Taurus pedals" ] }; { Name = "Neil Peart"; Instruments = [ "drums"; "orchestra bells"; "bell tree"; "timpani"; "gong"; "cowbells"; "temple blocks"; "wind chimes"; "crotales" ] } ]
Maybe you want one long list of every instrument the fellows in Rush 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 F# Fridays: map
and concat
:
let instruments = rush |> List.map (fun m -> m.Instruments) |> List.concat // val instruments : string list = // ["vocals"; "bass guitar"; "Oberheim polyphonic"; // "Minimoog"; "Moog Taurus pedals"; ...]
Heart and Mind United
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: collect
!
let instruments = rush |> List.collect (fun m -> m.Instruments) // val instruments : string list = // ["vocals"; "bass guitar"; "Oberheim polyphonic"; // "Minimoog"; "Moog Taurus pedals"; ...]
How ’bout that? The very same results all in one function!
So then, collect
performs a map and a concatenation, or a flatten, all in one function call. In fact, some languages name this operation flatMap
or flat_map
.
You’ve seen collect
examples here only in terms of lists, but the F#’s Array
and Seq
modules define the collect
operation, too:
One reply on “F# Friday – The collect Function”
[…] how the collections modules define the collect function? For instance, this is the List.collect […]