United Kingdom: +44 (0)208 088 8978

Introducing Collections.choose

Deepen your knowledge in F# with the `choose` method that helps you combine the powers of `filter` and `map` into one!

We're hiring Software Developers

Click here to find out more

Introduction

Today we are going to have a look at one of my more recent discoveries in the FSharp.Collections namespace. The 'choose' method.

Challenge

We are starting with a list of values and we want to filter the values to get a subset, then transform the values in that subset.

type InvoiceType =
    | Development
    | Training
    | Consultancy

type Invoice = {
    Id: int
    PaymentId: int option
    Type: InvoiceType
    Amount: decimal
    Date: DateTime
}

let invoices: Invoice list = [
//invoices not defined for brevity
]

The example we are using is a list but the same principal applies to sequence and array

Business requirement

What I need to get is the invoices, that

  • are for Training or Consultancy
  • have been paid
  • are dated before today

and return the amounts of those invoices so that we can total them

Conventional way

Usually we would reach for 2 very well known methods

List.filter
List.map

First we would filter out the values that we do not need and then transform/map the values into the invoice amount

let total =
    invoices
    |> List.filter (fun invoice ->
        (invoice.Type = Training || invoice.Type = Consultancy) &&
        invoice.PaymentId.IsSome && invoice.Date < DateTime.UtcNow)
    |> List.map (fun invoice -> invoice.Amount)
    |> List.sum

Using choose

The method that will do both the filtering and the mapping for you in a more F# kind of way

The signature is

(’T -> ‘U option) -> ’T seq -> ‘U seq

The first parameter does the:

mapping - (’T -> ‘U)
filtering - keeps the Some values of ‘U option and discard the None values

Knowing this we can now use this method together with a match statement

let total =
    invoices
    |> List.choose (fun invoice ->
        match invoice.Type, invoice.PaymentId, invoice.Date with
        | (Training | Consultancy), Some _, date when date < DateTime.UtcNow -> Some invoice.Amount
        | _, _, _ -> None)
    |> List.sum

Conclusion

There are many other methods in the FSharp.Collections namespace that you may not have encountered before so it is worth taking a look.