RxJS Operators decision table
Tags: javascript, rxjs
Published: 2020-12-11 09:25:19

{% twitter 1039590397943705602 %}

I like this joke yet I think it's obsolete.
I remember myself 4 years ago trying to comprehend all the concepts behind reactive programming, learning ways to write my streams and manipulate them using operators. It wasn't easy besides you had to keep up with all new changes (and there're a few of them). But today RxJS is a mature toolchain with a lot of learning materials, the amount of effort the team puts into making the library more accessible for common folks like me is truly impressive.

Yet it's still a common opinion that RxJS is not very friendly for newbies. Besides core concepts of reactive programming (that are not specific to RxJS), the number of operators available for a user might be intimidating.
As I stated above, the maintainers' team did and doing a great job simplifying and improving the API. And luckily we don't need to remember all of them as a tiny portion is actually needed for everyday use.

Still, you can find yourself a bit lost thinking of what operator should I use in certain cases. Don't worry the docs got you covered, it contains a super useful operator decision tree util https://rxjs.dev/operator-decision-tree. Answering simple question about your intentions with the stream it can suggest the most fitting operator for your case.

It's great, but I prefer the table view of the same tree. And unsurprisingly there's such a table https://xgrommx.github.io/rx-book/content/which_operator_do_i_use/index.html, but there's another "but" – it's a bit outdated thus I present you the up-to-date (at the time of writing this post) version of the same table:

# Creation operators

<table>
  <tr>
    <td rowspan="16">I want to create a new sequence</td>
    <td rowspan="2">using custom logic</td>
    <td colspan="2"></td>
    <td><a href="https://rxjs.dev/api/index/class/Observable#create">Observable (class)</a></td>
  </tr>
  <tr>
    <td colspan="2">that works like a for-loop</td>
    <td><a href="https://rxjs.dev/api/index/function/generate">generate</a></td>
  </tr>
  <tr>
    <td colspan="3">that throws an error</td>
    <td><a href="https://rxjs.dev/api/index/function/throwError">throwError</a></td>
  </tr>
  <tr>
    <td colspan="3">that completes</td>
    <td><a href="https://rxjs.dev/api/index/const/EMPTY">EMPTY (const)</a></td>
  </tr>
  <tr>
    <td colspan="3">that never does anything</td>
    <td><a href="https://rxjs.dev/api/index/const/NEVER">NEVER (const)</a></td>
  </tr>
  <tr>
    <td rowspan="2">from an event</td>
    <td colspan="2"></td>
    <td><a href="https://rxjs.dev/api/index/function/fromEvent">fromEvent</a></td>
  </tr>
  <tr>
    <td colspan="2">that uses custom functions to add and remove event handlers</td>
    <td><a href="https://rxjs.dev/api/index/function/fromEventPattern">fromEventPattern</a></td>
  </tr>
  <tr>
    <td rowspan="4"></td>
    <td>over the values in an array</td>
    <td>of object key/values</td>
    <td><a href="https://rxjs.dev/api/index/function/pairs">pairs</a></td>
  </tr>
  <tr>
    <td colspan="2">over values in a numeric range</td>
    <td><a href="https://rxjs.dev/api/index/function/range">range</a></td>
  </tr>
  <tr>
    <td colspan="2">over the values in an iterable, a Promise, array or array-like object</td>
    <td><a href="https://rxjs.dev/api/index/function/from">from</a></td>
  </tr>
  <tr>
    <td colspan="2">over arguments</td>
    <td><a href="https://rxjs.dev/api/index/function/of">of</a></td>
  </tr>
  <tr>
    <td rowspan="2">that emits values on a timer</td>
    <td colspan="2"></td>
    <td><a href="https://rxjs.dev/api/index/function/interval">interval</a></td>
  </tr>
  <tr>
    <td colspan="2">with an optional initial delay</td>
    <td><a href="https://rxjs.dev/api/index/function/timer">timer</a></td>
  </tr>
  <tr>
    <td rowspan="3">decided at subscribe-time</td>
    <td colspan="2">based on a boolean condition</td>
    <td><a href="https://rxjs.dev/api/index/function/iif">iif</a></td>
  </tr>
  <tr>
    <td rowspan="2">using custom logic</td>
    <td></td>
    <td><a href="https://rxjs.dev/api/index/function/defer">defer</a></td>
  </tr>
  <tr>
    <td>that depends on a resource</td>
    <td><a href="https://rxjs.dev/api/index/function/using">using</a></td>
  </tr>
  <tr>
    <td rowspan="7">I want to combine multiple sequences</td>
    <td colspan="3">and only receive values from the sequence that yields a value first</td>
    <td><a href="https://rxjs.dev/api/index/function/race">race</a></td>
  </tr>
  <tr>
    <td colspan="3">and be notified when all of them have finished</td>
    <td><a href="https://rxjs.dev/api/index/function/forkJoin">forkJoin</a></td>
  </tr>
  <tr>
    <td colspan="3">and output the values from all of them</td>
    <td><a href="https://rxjs.dev/api/index/function/merge">merge</a></td>
  </tr>
  <tr>
    <td rowspan="2">in order</td>
    <td colspan="2">reusing the latest value when unchanged</td>
    <td><a href="https://rxjs.dev/api/index/function/combineLatest">combineLatest</a></td>
  </tr>
  <tr>
    <td colspan="2">using each value only once</td>
    <td><a href="https://rxjs.dev/api/index/function/zip">zip</a></td>
  </tr>
  <tr>
    <td rowspan="2">by subscribing to each in order</td>
    <td colspan="2">when the previous sequence completes</td>
    <td><a href="https://rxjs.dev/api/index/function/concat">concat</a></td>
  </tr>
  <tr>
    <td colspan="2">regardless of whether the previous sequence completes or errors</td>
    <td><a href="https://rxjs.dev/api/index/function/onErrorResumeNext">onErrorResumeNext</a></td>
  </tr>
</table>

# Stream operators (ex instance operators)

<table>
  <tr>
    <td rowspan="69">
    <span class="rotate">Using an existing sequence</span>
    </td>
    <td colspan="3">I want to change each value</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/map">map</a></td>
  </tr>
  <tr>
    <td colspan="3">I want to pull a property off each value</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/pluck">pluck</a></td>
  </tr>
  <tr>
    <td colspan="3">I want to be notified of values without affecting them</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/tap">tap</a></td>
  </tr>
  <tr>
    <td rowspan="5">I want to include values</td>
    <td colspan="2">based on custom logic</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/filter">filter</a></td>
  </tr>
  <tr>
    <td rowspan="2">from the start of the sequence</td>
    <td></td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/take">take</a></td>
  </tr>
  <tr>
    <td class="tg-atax">based on custom logic</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/takeWhile">takeWhile</a></td>
  </tr>
  <tr>
    <td colspan="2">from the end of the sequence</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/takeLast">takeLast</a></td>
  </tr>
  <tr>
    <td colspan="2">until another sequence emits a value or completes</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/takeUntil">takeUntil</a></td>
  </tr>
  <tr>
    <td rowspan="14">I want to ignore values</td>
    <td colspan="2">altogether</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/ignoreElements">ignoreElements</a></td>
  </tr>
  <tr>
    <td rowspan="2">from the start of the sequence</td>
    <td></td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/skip">skip</a></td>
  </tr>
  <tr>
    <td class="tg-atax">based on custom logic</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/skipWhile">skipWhile</a></td>
  </tr>
  <tr>
    <td colspan="2">from the end of the sequence</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/skipLast">skipLast</a></td>
  </tr>
  <tr>
    <td colspan="2">until another sequence emits a value</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/skipUntil">skipUntil</a></td>
  </tr>
  <tr>
    <td rowspan="3">that have the same value as the previous</td>
    <td class="tg-atax"></td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/distinctUntilChanged">distinctUntilChanged</a></td>
  </tr>
  <tr>
    <td class="tg-atax">based on object by key</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/distinctUntilKeyChanged">distinctUntilKeyChanged</a></td>
  </tr>
  <tr>
    <td class="tg-atax">by selector</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/distinct">distinct</a></td>
  </tr>
  <tr>
    <td rowspan="2">that occur too frequently</td>
    <td class="tg-atax"></td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/throttle">throttle</a></td>
  </tr>
  <tr>
    <td class="tg-atax">based on duration</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/throttleTime">throttleTime</a></td>
  </tr>
  <tr>
    <td rowspan="2">by emitting the first value in each time window </td>
    <td></td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/debounce">debounce</a></td>
  </tr>
  <tr>
    <td class="tg-atax">based on due time</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/debounceTime">debounceTime</a></td>
  </tr>
  <tr>
    <td rowspan="2">by emitting the last value in each time window </td>
    <td></td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/audit">audit</a></td>
  </tr>
  <tr>
    <td class="tg-atax">based on due time</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/auditTime">auditTime</a></td>
  </tr>
  <tr>
    <td rowspan="3">I want to compute</td>
    <td rowspan="3">using custom logic</td>
    <td class="tg-atax">and only output the final value</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/reduce">reduce</a></td>
  </tr>
  <tr>
    <td class="tg-atax">and output the values as they are calculated</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/scan">scan</a></td>
  </tr>
  <tr>
    <td class="tg-atax">and output observables as they are calculated</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/mergeScan">mergeScan</a></td>
  </tr>
  <tr>
    <td rowspan="3">I want to wrap its messages with metadata</td>
    <td colspan="2">that describes each message</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/materialize">materialize</a></td>
  </tr>
  <tr>
    <td colspan="2">that includes the time past since the last value</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/timeInterval">timeInterval</a></td>
  </tr>
  <tr>
    <td colspan="2">that includes a timestamp</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/timestamp">timestamp</a></td>
  </tr>
  <tr>
    <td rowspan="2">after a period of inactivity</td>
    <td colspan="2">I want to throw an error</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/timeout">timeout</a></td>
  </tr>
  <tr>
    <td colspan="2">I want to switch to another sequence</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/timeoutWith">timeoutWith</a></td>
  </tr>
  <tr>
    <td class="tg-atax">I want ensure there is only one value</td>
    <td colspan="2">and throw an error if there are more or less than one value</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/single">single</a></td>
  </tr>
  <tr>
    <td rowspan="3">I want to only take the first value</td>
    <td colspan="2">and throw an error if there are no values</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/first">first</a></td>
  </tr>
  <tr>
    <td rowspan="2">within a time period</td>
    <td></td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/sample">sample</a></td>
  </tr>
  <tr>
    <td class="tg-atax">based on period</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/sampleTime">sampleTime</a></td>
  </tr>
  <tr>
    <td class="tg-atax">I want to only take the last value</td>
    <td colspan="2">and error if there are no values</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/last">last</a></td>
  </tr>
  <tr>
    <td colspan="3">I want to know how many values it contains</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/count">count</a></td>
  </tr>
  <tr>
    <td class="tg-atax"></td>
    <td colspan="2">by all of its values</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/every">every</a></td>
  </tr>
  <tr>
    <td colspan="2" rowspan="2">I want to delay messages by a specific amount of time</td>
    <td></td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/delay">delay</a></td>
  </tr>
  <tr>
    <td class="tg-atax">based on custom logic</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/delayWhen">delayWhen</a></td>
  </tr>
  <tr>
    <td rowspan="10">I want to group the values</td>
    <td colspan="2">until the sequence completes</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/toArray">toArray</a></td>
  </tr>
  <tr>
    <td rowspan="2">using custom logic</td>
    <td class="tg-atax">as arrays</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/buffer">buffer</a></td>
  </tr>
  <tr>
    <td class="tg-atax">as sequences</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/window">window</a></td>
  </tr>
  <tr>
    <td rowspan="2">in batches of a particular size</td>
    <td class="tg-atax">as arrays</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/bufferCount">bufferCount</a></td>
  </tr>
  <tr>
    <td class="tg-atax">as sequences</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/windowCount">windowCount</a></td>
  </tr>
  <tr>
    <td rowspan="2">based on time</td>
    <td class="tg-atax">as arrays</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/bufferTime">bufferTime</a></td>
  </tr>
  <tr>
    <td class="tg-atax">as sequences</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/windowTime">windowTime</a></td>
  </tr>
  <tr>
    <td rowspan="2">based on observable</td>
    <td class="tg-atax">as arrays</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/bufferWhen">bufferWhen</a></td>
  </tr>
  <tr>
    <td class="tg-atax">as sequences</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/windowWhen">windowWhen</a></td>
  </tr>
  <tr>
    <td class="tg-atax">based on a key</td>
    <td class="tg-atax">until the sequence completes</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/groupBy">groupBy</a></td>
  </tr>
  <tr>
    <td rowspan="4">I want to start a new sequence for each value</td>
    <td colspan="2">and emit the values from all sequences in parallel</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/mergeMap">mergeMap</a></td>
  </tr>
  <tr>
    <td colspan="2">and emit the values from each sequence in order</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/concatMap">concatMap</a></td>
  </tr>
  <tr>
    <td colspan="2">and cancel the previous sequence when a new value arrives</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/switchMap">switchMap</a></td>
  </tr>
  <tr>
    <td colspan="2">and recursively start a new sequence for each new value</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/expand">expand</a></td>
  </tr>
  <tr>
    <td class="tg-atax">I want to combine it with another</td>
    <td colspan="2">And be notified when both have completed</td>
    <td class="operator"><a href="https://rxjs.dev/api/index/function/forkJoin">forkJoin</a></td>
  </tr>
  <tr>
    <td rowspan="6">I want to share a subscription between multiple subscribers</td>
    <td colspan="2">using a specific subject implementation</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/multicast">multicast</a></td>
  </tr>
  <tr>
    <td colspan="2">and start it as soon as first subscriber arrives</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/share">share</a></td>
  </tr>
  <tr>
    <td colspan="2">and start it manually or imperatively</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/publish">publish</a></td>
  </tr>
  <tr>
    <td colspan="2">and supply the last value to future subscribers</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/publishLast">publishLast</a></td>
  </tr>
  <tr>
    <td colspan="2">and replay a default or the latest value to future subscribers</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/publishBehavior">publishBehavior</a></td>
  </tr>
  <tr>
    <td colspan="2">and replay n number of values to future subscribers</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/publishReplay">publishReplay</a></td>
  </tr>
  <tr>
    <td rowspan="3">when an error occurs</td>
    <td rowspan="2">I want to re-subscribe</td>
    <td></td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/retry">retry</a></td>
  </tr>
  <tr>
    <td class="tg-atax">based on custom logic</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/retryWhen">retryWhen</a></td>
  </tr>
  <tr>
    <td colspan="2">I want to start a new sequence</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/catchError">catchError</a></td>
  </tr>
  <tr>
    <td rowspan="2">when it completes</td>
    <td colspan="2">I want to re-subscribe</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/repeat">repeat</a></td>
  </tr>
  <tr>
    <td colspan="2">I want to start a new sequence</td>
    <td class="operator"><a href="https://rxjs.dev/api/index/function/concat">concat</a></td>
  </tr>
  <tr>
    <td class="tg-atax">when it completes or errors</td>
    <td colspan="2">I want to start a new sequence</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/onErrorResumeNext">onErrorResumeNext</a></td>
  </tr>
  <tr>
    <td class="tg-atax">when it completes, errors or unsubscribes</td>
    <td colspan="2">I want to execute a function</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/finalize">finalize</a></td>
  </tr>
  <tr>
    <td rowspan="2">I want to change the scheduler that routes</td>
    <td colspan="2">calls to subscribe</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/subscribeOn">subscribeOn</a></td>
  </tr>
  <tr>
    <td colspan="2">messages</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/observeOn">observeOn</a></td>
  </tr>
  <tr>
    <td rowspan="6">Using two sequences</td>
    <td class="tg-atax">I want to decide which to receive values from</td>
    <td colspan="2">based on which one has values first</td>
    <td class="operator"><a href="https://rxjs.dev/api/index/function/race">race</a></td>
  </tr>
  <tr>
    <td colspan="3">I want to determine if their values are equal</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/sequenceEqual">sequenceEqual</a></td>
  </tr>
  <tr>
    <td rowspan="3">I want to combine their values</td>
    <td colspan="2">only when the first sequence emits, using the latest value from each</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/withLatestFrom">withLatestFrom</a></td>
  </tr>
  <tr>
    <td rowspan="2">in order</td>
    <td class="tg-atax">reusing the latest value when unchanged</td>
    <td class="operator"><a href="https://rxjs.dev/api/operators/combineLatest">combineLatest</a></td>
  </tr>
  <tr>
    <td class="tg-atax">using each value only once</td>
    <td class="operator"><a href="https://rxjs.dev/api/index/function/zip">zip</a></td>
  </tr>
  <tr>
    <td colspan="3">I want to include values from both</td>
    <td class="operator"><a href="https://rxjs.dev/api/index/function/merge">merge</a></td>
  </tr>
</table>

If you're new to RxJS be sure to check out Eran's beginner guide post {% link https://dev.to/eransakal/understanding-rxjs-learn-from-examples-part-1-3m76 %}

_Keep hacking my dudes_ 😉