1 | import { Operator } from '../Operator';
|
---|
2 | import { Subscriber } from '../Subscriber';
|
---|
3 | import { Observable } from '../Observable';
|
---|
4 | import { MonoTypeOperatorFunction, TeardownLogic } from '../types';
|
---|
5 |
|
---|
6 | /* tslint:disable:max-line-length */
|
---|
7 | export function distinctUntilChanged<T>(compare?: (x: T, y: T) => boolean): MonoTypeOperatorFunction<T>;
|
---|
8 | export function distinctUntilChanged<T, K>(compare: (x: K, y: K) => boolean, keySelector: (x: T) => K): MonoTypeOperatorFunction<T>;
|
---|
9 | /* tslint:enable:max-line-length */
|
---|
10 |
|
---|
11 | /**
|
---|
12 | * Returns an Observable that emits all items emitted by the source Observable that are distinct by comparison from the previous item.
|
---|
13 | *
|
---|
14 | * If a comparator function is provided, then it will be called for each item to test for whether or not that value should be emitted.
|
---|
15 | *
|
---|
16 | * If a comparator function is not provided, an equality check is used by default.
|
---|
17 | *
|
---|
18 | * ## Example
|
---|
19 | * A simple example with numbers
|
---|
20 | * ```ts
|
---|
21 | * import { of } from 'rxjs';
|
---|
22 | * import { distinctUntilChanged } from 'rxjs/operators';
|
---|
23 | *
|
---|
24 | * of(1, 1, 2, 2, 2, 1, 1, 2, 3, 3, 4).pipe(
|
---|
25 | * distinctUntilChanged(),
|
---|
26 | * )
|
---|
27 | * .subscribe(x => console.log(x)); // 1, 2, 1, 2, 3, 4
|
---|
28 | * ```
|
---|
29 | *
|
---|
30 | * An example using a compare function
|
---|
31 | * ```typescript
|
---|
32 | * import { of } from 'rxjs';
|
---|
33 | * import { distinctUntilChanged } from 'rxjs/operators';
|
---|
34 | *
|
---|
35 | * interface Person {
|
---|
36 | * age: number,
|
---|
37 | * name: string
|
---|
38 | * }
|
---|
39 | *
|
---|
40 | * of<Person>(
|
---|
41 | * { age: 4, name: 'Foo'},
|
---|
42 | * { age: 7, name: 'Bar'},
|
---|
43 | * { age: 5, name: 'Foo'},
|
---|
44 | * { age: 6, name: 'Foo'},
|
---|
45 | * ).pipe(
|
---|
46 | * distinctUntilChanged((p: Person, q: Person) => p.name === q.name),
|
---|
47 | * )
|
---|
48 | * .subscribe(x => console.log(x));
|
---|
49 | *
|
---|
50 | * // displays:
|
---|
51 | * // { age: 4, name: 'Foo' }
|
---|
52 | * // { age: 7, name: 'Bar' }
|
---|
53 | * // { age: 5, name: 'Foo' }
|
---|
54 | * ```
|
---|
55 | *
|
---|
56 | * @see {@link distinct}
|
---|
57 | * @see {@link distinctUntilKeyChanged}
|
---|
58 | *
|
---|
59 | * @param {function} [compare] Optional comparison function called to test if an item is distinct from the previous item in the source.
|
---|
60 | * @return {Observable} An Observable that emits items from the source Observable with distinct values.
|
---|
61 | * @method distinctUntilChanged
|
---|
62 | * @owner Observable
|
---|
63 | */
|
---|
64 | export function distinctUntilChanged<T, K>(compare?: (x: K, y: K) => boolean, keySelector?: (x: T) => K): MonoTypeOperatorFunction<T> {
|
---|
65 | return (source: Observable<T>) => source.lift(new DistinctUntilChangedOperator<T, K>(compare, keySelector));
|
---|
66 | }
|
---|
67 |
|
---|
68 | class DistinctUntilChangedOperator<T, K> implements Operator<T, T> {
|
---|
69 | constructor(private compare: (x: K, y: K) => boolean,
|
---|
70 | private keySelector: (x: T) => K) {
|
---|
71 | }
|
---|
72 |
|
---|
73 | call(subscriber: Subscriber<T>, source: any): TeardownLogic {
|
---|
74 | return source.subscribe(new DistinctUntilChangedSubscriber(subscriber, this.compare, this.keySelector));
|
---|
75 | }
|
---|
76 | }
|
---|
77 |
|
---|
78 | /**
|
---|
79 | * We need this JSDoc comment for affecting ESDoc.
|
---|
80 | * @ignore
|
---|
81 | * @extends {Ignored}
|
---|
82 | */
|
---|
83 | class DistinctUntilChangedSubscriber<T, K> extends Subscriber<T> {
|
---|
84 | private key: K;
|
---|
85 | private hasKey: boolean = false;
|
---|
86 |
|
---|
87 | constructor(destination: Subscriber<T>,
|
---|
88 | compare: (x: K, y: K) => boolean,
|
---|
89 | private keySelector: (x: T) => K) {
|
---|
90 | super(destination);
|
---|
91 | if (typeof compare === 'function') {
|
---|
92 | this.compare = compare;
|
---|
93 | }
|
---|
94 | }
|
---|
95 |
|
---|
96 | private compare(x: any, y: any): boolean {
|
---|
97 | return x === y;
|
---|
98 | }
|
---|
99 |
|
---|
100 | protected _next(value: T): void {
|
---|
101 | let key: any;
|
---|
102 | try {
|
---|
103 | const { keySelector } = this;
|
---|
104 | key = keySelector ? keySelector(value) : value;
|
---|
105 | } catch (err) {
|
---|
106 | return this.destination.error(err);
|
---|
107 | }
|
---|
108 | let result = false;
|
---|
109 | if (this.hasKey) {
|
---|
110 | try {
|
---|
111 | const { compare } = this;
|
---|
112 | result = compare(this.key, key);
|
---|
113 | } catch (err) {
|
---|
114 | return this.destination.error(err);
|
---|
115 | }
|
---|
116 | } else {
|
---|
117 | this.hasKey = true;
|
---|
118 | }
|
---|
119 | if (!result) {
|
---|
120 | this.key = key;
|
---|
121 | this.destination.next(value);
|
---|
122 | }
|
---|
123 | }
|
---|
124 | }
|
---|