When you want a set number of items from the beginning of a sequence, you use Seq.take
or Seq.truncate
. When you don’t care so much about the number, but rather the nature of the items, use Seq.takeWhile
.
Seq.takeWhile
starts at the beginning of the sequence and applies a predicate to each item, one by one. It returns each item in a new sequence until it reaches an item that does not meet the predicate:
Note that Seq.takeWhile
does not return the item that fails to meet the predicate. It stops with the last item that meets the predicate.
Perhaps you have a sensor feeding you temperature readings once per minute:
type Reading = { Temperature : float Timestamp : DateTime } let readings = seq [ { Temperature = 89.5 Timestamp = DateTime(2015, 07, 19, 10, 0, 0) } { Temperature = 90.1 Timestamp = DateTime(2015, 07, 19, 10, 1, 0) } { Temperature = 89.9 Timestamp = DateTime(2015, 07, 19, 10, 2, 0) } { Temperature = 90.0 Timestamp = DateTime(2015, 07, 19, 10, 3, 0) } { Temperature = 90.1 Timestamp = DateTime(2015, 07, 19, 10, 4, 0) } { Temperature = -1000.0 Timestamp = DateTime(2015, 07, 19, 10, 5, 0) } { Temperature = -1000.0 Timestamp = DateTime(2015, 07, 19, 10, 6, 0) } { Temperature = 90.2 Timestamp = DateTime(2015, 07, 19, 10, 7, 0) } ]
Now to get all readings up until the thermometer returns the data-not-valid indicator, employ Seq.takeWhile
:
let valid = readings |> Seq.takeWhile (fun r -> r.Temperature <> -1000.0) // val valid: seq<Reading> = // [{Temperature = 89.5; // Timestamp = 7/19/2015 10:00:00 AM}; // {Temperature = 90.1; // Timestamp = 7/19/2015 10:01:00 AM}; // {Temperature = 89.9; // Timestamp = 7/19/2015 10:02:00 AM}; // {Temperature = 90.0; // Timestamp = 7/19/2015 10:03:00 AM}; // {Temperature = 90.1; // Timestamp = 7/19/2015 10:04:00 AM}]
Finally, even though Seq.take
throws an exception if you ask it for more elements than it can provide, Seq.takeWhile
does not balk if it never reaches an element that fails to meet the predicate:
let all = seq [1;3;4;7] |> Seq.takeWhile (fun n -> n < 10) // val all : seq<int> [1; 3; 4; 7]
As of F# 4.0, there are versions of takeWhile
in the Array
and List
modules, but as of this writing, the documentation on MSDN does not yet include them.
2 replies on “F# Friday – The Seq.takeWhile Function”
[…] F# Friday – The Seq.takeWhile Function – Brad Collins […]
[…] you have the same temperature sensor as the one in my post on Seq.takeWhile. This time, instead of once per minute, assume that it feeds you temperature readings once per […]