Scala Saturday – The Stream.dropWhile Method

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

Stream.dropWhile starts at the beginning of the stream and applies a predicate to each item, one by one. It does not start returning items in a new stream 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 stream from that point on:

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

Assume you have the same temperature sensor as the one in my post on Stream.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.

import java.time.{LocalDateTime, Month}
case class Reading(temperature: Double, timestamp: LocalDateTime)

val readings = Stream(
  Reading(-1000.0, LocalDateTime.of(2015, Month.JULY, 19, 10, 0, 0)),
  Reading(-1000.0, LocalDateTime.of(2015, Month.JULY, 19, 10, 0, 1)),
  Reading(-1000.0, LocalDateTime.of(2015, Month.JULY, 19, 10, 0, 2)),
  Reading(-1000.0, LocalDateTime.of(2015, Month.JULY, 19, 10, 0, 3)),
  Reading(-1000.0, LocalDateTime.of(2015, Month.JULY, 19, 10, 0, 4)),
  Reading(-1000.0, LocalDateTime.of(2015, Month.JULY, 19, 10, 0, 5)),
  Reading(90.1, LocalDateTime.of(2015, Month.JULY, 19, 10, 0, 6)),
  Reading(90.2, LocalDateTime.of(2015, Month.JULY, 19, 10, 0, 7)),
  Reading(90.2, LocalDateTime.of(2015, Month.JULY, 19, 10, 0, 8)),
  Reading(90.3, LocalDateTime.of(2015, Month.JULY, 19, 10, 0, 9)),
  Reading(90.2, LocalDateTime.of(2015, Month.JULY, 19, 10, 0, 10))
)

To drop all readings until the thermometer starts returning valid data, use Stream.dropWhile:

val valid = readings dropWhile (_.temperature == -1000.0)

// valid: scala.collection.immutable.Stream[Reading] = 
//   Stream(Reading(90.1,2015-07-19T10:00:06), 
//     Reading(90.2,2015-07-19T10:00:07), 
//     Reading(90.2,2015-07-19T10:00:08), 
//     Reading(90.3,2015-07-19T10:00:09), 
//     Reading(90.2,2015-07-19T10:00:10))

Finally, like Stream.takeWhile, Stream.dropWhile doesn’t balk if it never reaches an element that fails to meet the predicate. You just get an empty stream:

val none = Stream(1,3,4,7) dropWhile { _ < 10 }
// none: scala.collection.immutable.Stream[Int] = Stream()

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.