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.