F# Friday – The Seq.takeWhile Function

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:

Seq.takeWhile takes items from the beginning of a sequence until it reaches an item that does not meet the given predicate.
Taking Items from a Sequence While a Predicate Holds

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 thoughts on “F# Friday – The Seq.takeWhile 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.