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

RxJs filter with previous value

filter's iteratee function gets the current element and decides whether or not it will be present in the output. For example, to get only the even numbers, a simple filter function is:

observable // [1, 5, 2, 3]
  .pipe(
    filter((e) => e % 2 === 0) // [2]
  )

But what if you need to compare the current element with the previous one?

Pairwise and Map

pairwise emits the current element along with the previous one as an Array. This operator is useful when you want to compare them:

observable // [1, 5, 2, 3]
  .pipe(
    pairwise() // [[1, 5], [5, 2], [2, 3]]
  )

Using the result of pairwise, the filter following it has access to the previous element too. As an illustration, to keep only the elements that are greater than the previous one:

observable // [1, 5, 2, 3]
  .pipe(
    pairwise(), // [[1, 5], [5, 2], [2, 3]]
    filter(([prev, element]) => 
      element > prev
    ) // [[1, 5], [2, 3]]
  )

Then to get back to the original structure of the observable, use a map to extract the second element from the Array:

... // [[1, 5], [2, 3]]
map(([,e]) => e) // [5, 3]
The first element

As pairwise does not emit when there are no previous elements, the above structure discards the first element no matter what. To add custom logic to decide whether that should be included or not, use startWith and an if.

Define a Symbol, so that it won't collide with something the observable emits and check for that:

const empty = Symbol();

observable // [1, 5, 2, 3]
  .pipe(
    startWith(empty), // [empty, 1, 5, 2, 3]
    pairwise(), // [[empty, 1], [1, 5], [5, 2], [2, 3]]
    filter(([prev, element]) => {
      if (prev === empty) {
        // Deal with the first element
        return true;
      }else {
        // Deal with subsequent elements
        return element > prev;
      }
    }), // [[empty, 1], [1, 5], [2, 3]]
    map(([,e]) => e) // [1, 5, 3]
  )
Try it
References
Learn more: