24 Jul 2024
5 min

distinctUntilChanged – RxJS Reference

distinctUntilChanged

distinctUntilChanged emits all items emitted by the source observable that are distinct by comparison from the previous item. By default, it uses simple comparison operator that doesn’t check any keys, so object references must match for an object to be considered the same.

The operator takes an optional comparison function that will be called to test if an item is distinct from the previous item. The function receives the current and the previous values as parameters and must return a boolean value that determines whether or not that value should be emitted to the observer or be ignored. Since it’s a comparison function, if it returns true, it means that values are the same and hence the value is ignored, otherwise it’s sent to the observer.

Please note that this operator doesn’t select unique values, it simply prevents two equal values in a row. It means, that if you have a sequence {1,2,3,2} with the number 2 appearing two times, it will still end up in the resulting output twice because those numbers do not follow each other.

For more sophisticated use cases you can use the filter operator instead.

distinctUntilChanged operator works in the following way:

  1. Subscribe to a source observable
  2. When a new value arrives from a source observable, run a comparison with the previous value either using a comparison function if provided or the strict comparison operator
  3. If values match, ignore the current value, otherwise, send the value to the observer
  4. Once the source observable completes, send the complete notification to the observer
  5. If the source observable throws an error, send the error notification to the observer

Usage

One common usage of the distinctUntilChanged is to prevent duplicate emissions of the values on inputs. Consider the following setup:

const input = document.createElement('input');
document.body.appendChild(input);

fromEvent(input, 'input').pipe(
   debounceTime(1000),
   map((event: any) => event.target.value)
).subscribe((value) => console.log(value));

Using debounceTime(1000) means we only receive a value when the user stopped typing for 1 second. However, during this 1 second the user can write some characters but then erase them. For example, a user types ab, then adds c, then removes it c within the 1s interval, but you’ll still both ab in the resulting set {ab, ab}.

By simply adding distinctUntilChanged we can prevent that:

fromEvent(input, 'input').pipe(
   debounceTime(1000),
   map((event: any) => event.target.value),
   distinctUntilChanged()
).subscribe((value) => console.log(value));

The operator prevents emission of the second ab since it’s the same as the last emission.

Playground

Additional resources

See also

Frequently asked questions

How do I prevent duplicate consecutive values in an RxJS stream?

Use the distinctUntilChanged() operator in your pipe to suppress any value that is equal to the previously emitted value. By default it compares with strict equality (===), so object references must match to be treated as the same. For example, debounceTime(1000) followed by distinctUntilChanged() stops an input's value from being emitted twice when the user types and then erases characters within the debounce window.

What is the difference between distinctUntilChanged and distinct in RxJS?

distinctUntilChanged() only compares each value with the immediately preceding one, so it removes repeated values in a row, while the values can still reappear later in the stream. For example, the sequence 1, 2, 3, 2 keeps both 2s. If you need to filter out any value seen before, you should use a different approach, such as the filter operator with your own tracking logic.

How do I use a custom comparison function with distinctUntilChanged?

Pass a comparison function as the argument to distinctUntilChanged(). It receives the previous and current values and returns true when they are considered the same, which causes the current value to be ignored. Returning false lets the value pass to the subscriber. This is useful when you need to compare objects by a specific key rather than by reference.

Why does distinctUntilChanged not filter out equal object values in my stream?

By default distinctUntilChanged() uses strict comparison, so two objects are only considered equal if they are the same reference. Objects with identical properties but different references will both be emitted. To compare objects by their contents or a key, supply a comparison function as the operator's argument that checks the properties you care about.

When should I use the filter operator instead of distinctUntilChanged in RxJS?

Use filter() when your deduplication rule is more complex than comparing the current value with the previous one. distinctUntilChanged() is designed only for preventing two equal values in a row, so it cannot select unique values across the whole sequence. A filter() call with custom state gives you full control over which values are passed through.

Share this post

Sign up for our newsletter

Stay up-to-date with the trends and be a part of a thriving community.