For more tips like this, sign up to the weekly newsletter!

The difference between filter and takeWhile for lazy collections

Functionality-wise, filter and takeWhile are obviously different. The former returns all the elements a condition is true, while the latter does so only until the first false one.

const seq = Immutable.Seq([1, 2, 3, 2, 1]);

seq
  .filter((n) => n < 3) // 1, 2, 2, 1
  
seq
  .takeWhile((n) => n < 3) // 1, 2
Performance difference

But they also differ performance-wise. takeWhile can stop as soon as the first false is found, while filter must go on and evaluate all the elements, even if none pass the check.

To evaluate the difference in runtime, let's start with a Seq with many elements and measure the time each of the functions finishes:

const bigSeq = Immutable.Range(0, 10000000);

console.log(
  bigSeq
    .filter((n) => n < 3)
); // ~ 80 ms

console.log(
  bigSeq
    .takeWhile((n) => n < 3)
); // ~ 3 ms

Note: console.logs are needed to evaluate the expressions. Seq is a lazy structure; it needs a nudge to start working.

Implications for infinite collections

Seqs with a finite amount of elements differ only in performance (apart from the functionality, of course). But in some cases using the right function can be the difference between a successful calculation and an infinite loop.

For example, let's get all the primes that have at most three digits:

const primes = ... // Seq [2, 3, 5, 7, ...]
  
console.log(
  primes
    .takeWhile((n) => String(n).split("").length <= 3)
); // 2, 3, ..., 997

In this case, using filter instead of takeWhile makes the calculation to run indefinitely, even though their theoretical result is the same.

Try it
Learn more: