RxJS Mastery – #24 partition

RxJS partition operator lesson title

The RxJS partition operator accepts an Observable and splits it into two. The split is done based on a predicate. The first Observable returned by partition emits the values satisfying the predicate, the second one is about the values that do not satisfy the predicate.

RxJS partition explained 🎓

The simplest demonstration of partition is to split numbers into even and odd ones. As a first argument of partition we pass the Observable with the numbers. The second argument is the predicate, i.e. a function operating on values of the first argument and returning a boolean.

const [evens$, odds$] = partition(
    of(1, 2, 3, 4, 5, 6, 7),
    v => v % 2 === 0
);

evens$.subscribe(console.log);
odds$.subscribe(console.log);
// 2, 4, 6
// 1, 3, 5, 7

We see that the return type is an array. And the array has exactly two elements. The first element is an Observable containing even numbers, i.e. the values satisfying the condition. On the other hand the second element contains an Observable that will emit the odd numbers.

Importantly, the subscription to the source Observable happens separately for each of the two output Observables. That means in above example we have two subscriptions to of(1, 2, 3, 4, 5, 6, 7). This is also one of the discussion around partition. There is no multicast that would only require one subscription.

What problems does the RxJS partition operator solve? 🚧

The partition operator acts similar to filter. Although with filter you are only keep the values satisfying a predicate and the rest is lost. Hence, partition makes sense when you also want to emit the values not matching a certain condition (beside those that match the condition).

You can for example split a stream of operations into two. One for operations that were successful and another one for those who weren’t. Another example were the partition could help is in case of events.

const [clicksOnDiv$, otherClicks$] = partition(
    fromEvent(document, 'click'),
    ev => ev?.target?.tagName === 'DIV'
);

How to test code using the partition operator🚦

There is nothing really special around the partition operator in terms of testing. Just be aware that the two created Observables can only complete once the source Observable completes. Hence the evens$ stream is not complete when the number 4 is emitted but one frame later. For the odds$ Observable the completion happens at the same time as the emission of 5. This is because the take(6) has received 6 values and can therefore complete at the same time the last value is emitted.

it('marbles testing', () => {
    testScheduler.run((helpers) => {
        const { expectObservable } = helpers;

        const [evens$, odds$] = partition(
            interval(1).pipe(take(6)),
            x => x % 2 === 0
        );

        expectObservable(evens$).toBe('-a-b-c|', {a: 0, b: 2, c: 4});
        expectObservable(odds$).toBe(' --a-b-(c|)', {a: 1, b: 3, c: 5});
    });
});

Exercise for the partition operator 💪

What happens if the subscription to odds$ only happens after 3ms? Will odds$ still emit the number 1?

const [evens$, odds$] = partition(
    interval(1).pipe(take(6)),
    x => x % 2 === 0
);

As always the code examples and exercise solution can be found on GitHub.