distinct.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. import { OuterSubscriber } from '../OuterSubscriber';
  2. import { subscribeToResult } from '../util/subscribeToResult';
  3. import { Set } from '../util/Set';
  4. /**
  5. * Returns an Observable that emits all items emitted by the source Observable that are distinct by comparison from previous items.
  6. *
  7. * If a keySelector function is provided, then it will project each value from the source observable into a new value that it will
  8. * check for equality with previously projected values. If a keySelector function is not provided, it will use each value from the
  9. * source observable directly with an equality check against previous values.
  10. *
  11. * In JavaScript runtimes that support `Set`, this operator will use a `Set` to improve performance of the distinct value checking.
  12. *
  13. * In other runtimes, this operator will use a minimal implementation of `Set` that relies on an `Array` and `indexOf` under the
  14. * hood, so performance will degrade as more values are checked for distinction. Even in newer browsers, a long-running `distinct`
  15. * use might result in memory leaks. To help alleviate this in some scenarios, an optional `flushes` parameter is also provided so
  16. * that the internal `Set` can be "flushed", basically clearing it of values.
  17. *
  18. * @example <caption>A simple example with numbers</caption>
  19. * Observable.of(1, 1, 2, 2, 2, 1, 2, 3, 4, 3, 2, 1)
  20. * .distinct()
  21. * .subscribe(x => console.log(x)); // 1, 2, 3, 4
  22. *
  23. * @example <caption>An example using a keySelector function</caption>
  24. * interface Person {
  25. * age: number,
  26. * name: string
  27. * }
  28. *
  29. * Observable.of<Person>(
  30. * { age: 4, name: 'Foo'},
  31. * { age: 7, name: 'Bar'},
  32. * { age: 5, name: 'Foo'})
  33. * .distinct((p: Person) => p.name)
  34. * .subscribe(x => console.log(x));
  35. *
  36. * // displays:
  37. * // { age: 4, name: 'Foo' }
  38. * // { age: 7, name: 'Bar' }
  39. *
  40. * @see {@link distinctUntilChanged}
  41. * @see {@link distinctUntilKeyChanged}
  42. *
  43. * @param {function} [keySelector] Optional function to select which value you want to check as distinct.
  44. * @param {Observable} [flushes] Optional Observable for flushing the internal HashSet of the operator.
  45. * @return {Observable} An Observable that emits items from the source Observable with distinct values.
  46. * @method distinct
  47. * @owner Observable
  48. */
  49. export function distinct(keySelector, flushes) {
  50. return (source) => source.lift(new DistinctOperator(keySelector, flushes));
  51. }
  52. class DistinctOperator {
  53. constructor(keySelector, flushes) {
  54. this.keySelector = keySelector;
  55. this.flushes = flushes;
  56. }
  57. call(subscriber, source) {
  58. return source.subscribe(new DistinctSubscriber(subscriber, this.keySelector, this.flushes));
  59. }
  60. }
  61. /**
  62. * We need this JSDoc comment for affecting ESDoc.
  63. * @ignore
  64. * @extends {Ignored}
  65. */
  66. export class DistinctSubscriber extends OuterSubscriber {
  67. constructor(destination, keySelector, flushes) {
  68. super(destination);
  69. this.keySelector = keySelector;
  70. this.values = new Set();
  71. if (flushes) {
  72. this.add(subscribeToResult(this, flushes));
  73. }
  74. }
  75. notifyNext(outerValue, innerValue, outerIndex, innerIndex, innerSub) {
  76. this.values.clear();
  77. }
  78. notifyError(error, innerSub) {
  79. this._error(error);
  80. }
  81. _next(value) {
  82. if (this.keySelector) {
  83. this._useKeySelector(value);
  84. }
  85. else {
  86. this._finalizeNext(value, value);
  87. }
  88. }
  89. _useKeySelector(value) {
  90. let key;
  91. const { destination } = this;
  92. try {
  93. key = this.keySelector(value);
  94. }
  95. catch (err) {
  96. destination.error(err);
  97. return;
  98. }
  99. this._finalizeNext(key, value);
  100. }
  101. _finalizeNext(key, value) {
  102. const { values } = this;
  103. if (!values.has(key)) {
  104. values.add(key);
  105. this.destination.next(value);
  106. }
  107. }
  108. }
  109. //# sourceMappingURL=distinct.js.map