RxJS - Dependent Observable for Multiple Matches

02/16/2021, Tue
Categories: #JavaScript
Tags: #RxJS

Give Precedence to Greater Satisfied Criteria

Observables tend to be created from other observables through transformations by operators in a pipe. On some occasions, one observable is created from another, it creates a chain where you have the latter observable and initial observable both triggering when the initial base condition is met. However, there might be cases, where you would only want the latter observable to trigger because you want to find the observable which matches the most conditions.

Take for example of the number 24. This number's factors are to be determined, and multiple factors are known to work from a list that are divisible, but we are only interested in the largest factor from a list of numbers (2, 4, 6). 24 is first divisible by 2, then by 4 and then by 6.

Example below:

import { of, operators, merge } from 'rxjs';
import { map, filter, takeLast } from 'rxjs/operators';

const divisibleNum = { num: 24 };
const divisible$ = of(divisibleNum);

const divisibleBy2$ = divisible$
  .pipe(
    filter((data) => {
      return data.num % 2 === 0;
    }),
    map((data) => {
      return {
        ...data,
        from: 2
      }
    })
  );

divisibleBy2$
  .subscribe(() => {
    console.log("Divisible by 2");
  });

const divisibleBy4and2$ = divisibleBy2$
  .pipe(
    filter((data) => {
      return data.num % 4 === 0;
    }),
    map((data) => {
      return {
        ...data,
        from: 4
      }
    })
  );

divisibleBy4and2$
  .subscribe(() => {
    console.log("Divisible by 4 and 2");
  });

const divisibleBy6And4And2$ = divisibleBy4and2$
  .pipe(
    filter((data) => {
      return data.num % 6 === 0;
    }),
    map((data) => {
      return {
        ...data,
        from: 6
      }
    })
  );

divisibleBy6And4And2$
  .subscribe(() => {
    console.log("Divisible by 6, 4, and 2");
  });

// Since multiple divisible observables will trigger, and the
// last observable in the merge array is what gets triggered
// after all the prior observable in the list have triggered,
// get the last return.
merge(divisibleBy2$, divisibleBy4and2$, divisibleBy6And4And2$)
  .pipe(
    takeLast(1)
  )
  .subscribe((data) => {
    console.log("Divisible from divisible factor", data.from, "and below");
  });

/* LOGS 

"Divisible by 2"
"Divisible by 4 and 2"
"Divisible by 6, 4, and 2"
"Divisible from divisible factor", 6, "and below"

*/

In the example above, each new observable is created from the one before it, so each divisibility condition is stricter than the one before. The checks above does not provide a general divisibility of a factor but suggests a cumulative one.

The behavior above resembles what you would see if you use a switch statement in JavaScript with the more stringent case declarations placed closer to the beginning of the switch block.

Online Demo