F# Friday – The Seq.skipWhile Function

Just as the analog to Seq.take is Seq.skip, the analog to Seq.takeWhile is Seq.skipWhile. That is, when you don’t care so much about skipping a certain number of items, but rather a certain kind of items.

Seq.skipWhile starts at the beginning of the sequence and applies a predicate to each item, one by one. It does not start returning items in a new sequence until it reaches an item that does not meet the predicate. Then it stops checking elements against the predicate and returns every item in the sequence from that point on:

Seq.skipWhile skips items at the beginning of a sequence until it reaches an item that does not meet the given predicate.
Skipping Items in a Sequence While a Predicate Holds

Assume 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 second. Add to that the idea that the sensor has a few seconds of boot-up time in which it sends you -1000.0—the indication that the current reading is invalid—until it has fully booted and can start sending good temperature data.

type Reading = {
    Temperature : float
    Timestamp : DateTime
}

let readings = 
    seq [
        { Temperature = -1000.0
          Timestamp = DateTime(2015, 07, 19, 10, 0, 0) }
        { Temperature = -1000.0
          Timestamp = DateTime(2015, 07, 19, 10, 0, 1) }
        { Temperature = -1000.0
          Timestamp = DateTime(2015, 07, 19, 10, 0, 2) }
        { Temperature = -1000.0
          Timestamp = DateTime(2015, 07, 19, 10, 0, 3) }
        { Temperature = -1000.0
          Timestamp = DateTime(2015, 07, 19, 10, 0, 4) }
        { Temperature = -1000.0
          Timestamp = DateTime(2015, 07, 19, 10, 0, 5) }
        { Temperature = 90.1
          Timestamp = DateTime(2015, 07, 19, 10, 0, 6) }
        { Temperature = 90.2
          Timestamp = DateTime(2015, 07, 19, 10, 0, 7) }
        { Temperature = 90.2
          Timestamp = DateTime(2015, 07, 19, 10, 0, 8) }
        { Temperature = 90.1
          Timestamp = DateTime(2015, 07, 19, 10, 0, 9) }
        { Temperature = 90.3
          Timestamp = DateTime(2015, 07, 19, 10, 0, 10) }
    ]

To skip all readings until the thermometer starts returning valid data, use Seq.skipWhile:

let valid = 
    readings
    |> Seq.skipWhile (fun r -> r.Temperature = -1000.0)

// val valid: seq<Reading> =
//  [{Temperature = 90.1;
//    Timestamp = 7/19/2015 10:00:06 AM};
//   {Temperature = 90.2;
//    Timestamp = 7/19/2015 10:00:07 AM};
//   {Temperature = 90.2;
//    Timestamp = 7/19/2015 10:00:08 AM};
//   {Temperature = 90.1;
//    Timestamp = 7/19/2015 10:00:09 AM};
//   {Temperature = 90.3;
//    Timestamp = 7/19/2015 10:00:10 AM}]

Finally, even though Seq.skip throws an exception if you ask it to skip more elements than the sequence contains, Seq.skipWhile does not balk if it never reaches an element that fails to meet the predicate. You just get an empty sequence:

let none = seq [1;3;4;7]
           |> Seq.skipWhile (fun n -> n < 10)
// val none : seq<int> = seq []

As of F# 4.0, there are versions of skipWhile in the Array and List modules, but as of this writing, the documentation at MSDN does not yet include them.

One thought on “F# Friday – The Seq.skipWhile 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.