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]
)