Filter Redundant Observable Emissions with RxJS distinct

André Staltz
InstructorAndré Staltz
Share this video with your friends

Social Share Links

Send Tweet
Published 8 years ago
Updated 3 years ago

Operator distinct() and its variants are an important type of Filtering operator. This lessons shows how they work and in what cases are they useful.

🚨 Since we are importing interval from RxJS, we don't need to preface our Observables with Rx.Observable. You can no longer .{operator}, you need to .pipe({operator}) instead.

[00:00] More on filtering operators, let's take a look now at distinct. As the name hints distinct allows only unique values on the output observable. So suppose you have this input observable over here, and if we draw the marble diagram for distinct it looks like this. Once it sees a it will keep a in the registry, and then it will emit a. Then when it sees b, it will check if b is in the registry. Because it's not, the registry has an array of only a, then it will emit b, and now the registry has a and b.

[00:36] So then when it sees a again, it check that a is already in the registry, and that's why it will skip a. Then when it sees c, c is not in the registry, it will put c in the registry and it will emit c. B was already in the registry, it skips it, and then it's complete. That was distinct. If we run this, we will see a, b, and c only. But let's suppose you have this case here, uppercase A. Because uppercase A is actually distinct to lower case a, it would emit this uppercase A.

[01:18] In those cases distinct allows you to give a custom compare function as an argument in order to tell what does equality mean. In this case we would like to say uppercase A is actually the same as lowercase a. We do that by specifying what does equality mean for us, and here we can give a function with two arguments, and we can check if their lowercase versions are the same. If we run this we should see that this uppercase A was skipped it already happened in the past according to our equality definition, like that.

[02:06] Also distinct, besides taking this argument, you ca also give a flusher argument. This here is an observable, and this observable dictates when should the registry be cleared. Let's write that flusher observable. If we draw this flusher observable in a marble diagram it looks like this, and it doesn't complete. So once this 0happen, it will clear the registry and that's why it will emit a, and also c it will emit that, and b did not happen since this time, so that's why it will also this lowercase b, like that.

[02:54] However, distinct is not that often used as you may think it is. Actually most of the cases people want instead this DistinctUntilChanged, which is a variant of distinct. DistinctUntilChanged does not keep a registry of everything from the past, but it keeps a registry of just the most recent events from the past. Let's take a look how it is in a marble diagram with the lowercase a here. Once it sees lowercase a it is distinct to what happened in the past which is nothing, that's why emitted.

[03:34] B is distinct to a, that why emits b, a is distinct to b, emits a, also c is, and b is. Let's say you would have instead of c here, you would have a, and in this case because a is the same as what just happened, it would skip this lowercase a. That is DistinctUntilChanged. If we run this, we will see just a, b, a, b. In a nutshell distinct and DistinctUntilChanged as rate limiting operators, let's say you would have many As here happening fast or really frequently.

[04:18] DistinctUntilChanged would just ignore all of these, and emit like that. Or it can be used also has just a filtering function in order to avoid redundant events, that's also the biggest use case for DistinctUntilChanged.

Jon
Jon
~ 7 years ago

For people using TypeScript, you have have to import 'rxjs/add/operator/distinct'; Distinct was not included when importing 'rxjs/Rx'; and my build was erroring out.

Mike
Mike
~ 7 years ago
var result = foo.distinct((x,y) => x.toLowerCase() === y.toLowerCase());

gives: "error: TypeError: y is undefined"

Should be:

var result = foo.distinct((x) => x.toLowerCase());
André Staltz
André Staltzinstructor
~ 7 years ago

Hi Mike. That's correct. We should update the lesson to use a 'keySelector' function, not a 'compare' function.

Markdown supported.
Become a member to join the discussionEnroll Today