Filtering over a sequence of values omits values that do not meet certain criteria. Mapping over a sequence of values transforms each value into another value. What if you could do both at the same time—filter out unwanted values, but transform the ones that are left? You can with Seq.choose
.
Whereas Seq.filter
takes a predicate—a function that takes a value and returns a Boolean—Seq.choose
takes a function that takes a value and returns an Option
. If that Option
is None
, then Seq.choose
discards it. If it is Some
, then Seq.choose
extracts the value from the Some
and returns it as the next element in the output sequence.
let f = fun n -> match n % 2 with | 0 -> Some (n * n) | _ -> None let squaredEvens = seq [4..7] |> Seq.choose f // val squaredEvens : seq<int> = seq [16; 36]
The following graphic illustrates what is going on:
OK, so Seq.choose
performs a filter and a map all in one. Why not just call Seq.filter
and then Seq.map
? One example I’ve seen is when you’re pattern matching and destructuring and then only using one/some of the potential match cases. Perhaps you have a discriminated union representing orders that were either fulfilled or cancelled before fulfillment:
type Order = | Fulfilled of id : string * total : decimal | Cancelled of id : string * total : decimal
You’d like to know how many dollars you “lost” in cancelled orders. Use Seq.choose
to extract the dollar value of each cancelled order, and then sum them:
let orders = [ Fulfilled ("fef3356074b4", 28.50m) Fulfilled ("2605c9988f1d", 88.25m) Cancelled ("94edac47971f", 22.01m) Fulfilled ("2a1ff57b8f46", 39.30m) Fulfilled ("9ee0a3e3da3a", 27.97m) Cancelled ("db5dc439ad93", 99.49m) Fulfilled ("08d58811ed36", 53.72m) Cancelled ("63ebd07475ca", 93.66m) Cancelled ("12d16ae9c112", 7.79m) Fulfilled ("c5ecedaedb0e", 87.21m) ] let cancelledDollars = orders |> Seq.choose (function | Cancelled (_, dollars) -> Some dollars | _ -> None) |> Seq.sum // val cancelledDollars : decimal = 222.95M
One reply on “F# Friday – Seq.choose”
[…] F# Friday – Seq.choose – Brad Collins […]