Categories
Tech

F# Friday – The collect Function

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:

Illustrates mapping a list of musicians (geddy, alex, neil) to a list of lists of the instruments that each musician plays ((vocals, bass guitar, ...), (electric guitars, acoustic guitars, ...), (drums, orchestra bells, ...)) and then a flattening of those nested lists into one long list
Collecting Musicians’ Instruments into One Long List

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”

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.