2edfa02b14165d7c7ee5ef66ba2108ce203d0a116a4ce38c2be3ab2d88e7b8cb2dea8b866478c36f83553439edac37691135406d9414e00b11a58d45b17224 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. import { Observable } from '../Observable';
  2. import { EmptyError } from '../util/EmptyError';
  3. import { MonoTypeOperatorFunction, OperatorFunction, TruthyTypesOf } from '../types';
  4. import { SequenceError } from '../util/SequenceError';
  5. import { NotFoundError } from '../util/NotFoundError';
  6. import { operate } from '../util/lift';
  7. import { createOperatorSubscriber } from './OperatorSubscriber';
  8. export function single<T>(predicate: BooleanConstructor): OperatorFunction<T, TruthyTypesOf<T>>;
  9. export function single<T>(predicate?: (value: T, index: number, source: Observable<T>) => boolean): MonoTypeOperatorFunction<T>;
  10. /**
  11. * Returns an observable that asserts that only one value is
  12. * emitted from the observable that matches the predicate. If no
  13. * predicate is provided, then it will assert that the observable
  14. * only emits one value.
  15. *
  16. * If the source Observable did not emit `next` before completion, it
  17. * will emit an {@link EmptyError} to the Observer's `error` callback.
  18. *
  19. * In the event that two values are found that match the predicate,
  20. * or when there are two values emitted and no predicate, it will
  21. * emit a {@link SequenceError} to the Observer's `error` callback.
  22. *
  23. * In the event that no values match the predicate, if one is provided,
  24. * it will emit a {@link NotFoundError} to the Observer's `error` callback.
  25. *
  26. * ## Example
  27. *
  28. * Expect only `name` beginning with `'B'`
  29. *
  30. * ```ts
  31. * import { of, single } from 'rxjs';
  32. *
  33. * const source1 = of(
  34. * { name: 'Ben' },
  35. * { name: 'Tracy' },
  36. * { name: 'Laney' },
  37. * { name: 'Lily' }
  38. * );
  39. *
  40. * source1
  41. * .pipe(single(x => x.name.startsWith('B')))
  42. * .subscribe(x => console.log(x));
  43. * // Emits 'Ben'
  44. *
  45. *
  46. * const source2 = of(
  47. * { name: 'Ben' },
  48. * { name: 'Tracy' },
  49. * { name: 'Bradley' },
  50. * { name: 'Lincoln' }
  51. * );
  52. *
  53. * source2
  54. * .pipe(single(x => x.name.startsWith('B')))
  55. * .subscribe({ error: err => console.error(err) });
  56. * // Error emitted: SequenceError('Too many values match')
  57. *
  58. *
  59. * const source3 = of(
  60. * { name: 'Laney' },
  61. * { name: 'Tracy' },
  62. * { name: 'Lily' },
  63. * { name: 'Lincoln' }
  64. * );
  65. *
  66. * source3
  67. * .pipe(single(x => x.name.startsWith('B')))
  68. * .subscribe({ error: err => console.error(err) });
  69. * // Error emitted: NotFoundError('No values match')
  70. * ```
  71. *
  72. * @see {@link first}
  73. * @see {@link find}
  74. * @see {@link findIndex}
  75. * @see {@link elementAt}
  76. *
  77. * @throws {NotFoundError} Delivers a `NotFoundError` to the Observer's `error`
  78. * callback if the Observable completes before any `next` notification was sent.
  79. * @throws {SequenceError} Delivers a `SequenceError` if more than one value is
  80. * emitted that matches the provided predicate. If no predicate is provided, it
  81. * will deliver a `SequenceError` if more than one value comes from the source.
  82. * @throws {EmptyError} Delivers an `EmptyError` if no values were `next`ed prior
  83. * to completion.
  84. *
  85. * @param predicate A predicate function to evaluate items emitted by the source
  86. * Observable.
  87. * @return A function that returns an Observable that emits the single item
  88. * emitted by the source Observable that matches the predicate.
  89. */
  90. export function single<T>(predicate?: (value: T, index: number, source: Observable<T>) => boolean): MonoTypeOperatorFunction<T> {
  91. return operate((source, subscriber) => {
  92. let hasValue = false;
  93. let singleValue: T;
  94. let seenValue = false;
  95. let index = 0;
  96. source.subscribe(
  97. createOperatorSubscriber(
  98. subscriber,
  99. (value) => {
  100. seenValue = true;
  101. if (!predicate || predicate(value, index++, source)) {
  102. hasValue && subscriber.error(new SequenceError('Too many matching values'));
  103. hasValue = true;
  104. singleValue = value;
  105. }
  106. },
  107. () => {
  108. if (hasValue) {
  109. subscriber.next(singleValue);
  110. subscriber.complete();
  111. } else {
  112. subscriber.error(seenValue ? new NotFoundError('No matching values') : new EmptyError());
  113. }
  114. }
  115. )
  116. );
  117. });
  118. }