import { Observable } from '../Observable';
|
import { EmptyError } from '../util/EmptyError';
|
|
import { MonoTypeOperatorFunction, OperatorFunction, TruthyTypesOf } from '../types';
|
import { SequenceError } from '../util/SequenceError';
|
import { NotFoundError } from '../util/NotFoundError';
|
import { operate } from '../util/lift';
|
import { createOperatorSubscriber } from './OperatorSubscriber';
|
|
export function single<T>(predicate: BooleanConstructor): OperatorFunction<T, TruthyTypesOf<T>>;
|
export function single<T>(predicate?: (value: T, index: number, source: Observable<T>) => boolean): MonoTypeOperatorFunction<T>;
|
|
/**
|
* Returns an observable that asserts that only one value is
|
* emitted from the observable that matches the predicate. If no
|
* predicate is provided, then it will assert that the observable
|
* only emits one value.
|
*
|
* In the event that the observable is empty, it will throw an
|
* {@link EmptyError}.
|
*
|
* In the event that two values are found that match the predicate,
|
* or when there are two values emitted and no predicate, it will
|
* throw a {@link SequenceError}
|
*
|
* In the event that no values match the predicate, if one is provided,
|
* it will throw a {@link NotFoundError}
|
*
|
* ## Example
|
*
|
* Expect only `name` beginning with `'B'`
|
*
|
* ```ts
|
* import { of, single } from 'rxjs';
|
*
|
* const source1 = of(
|
* { name: 'Ben' },
|
* { name: 'Tracy' },
|
* { name: 'Laney' },
|
* { name: 'Lily' }
|
* );
|
*
|
* source1
|
* .pipe(single(x => x.name.startsWith('B')))
|
* .subscribe(x => console.log(x));
|
* // Emits 'Ben'
|
*
|
*
|
* const source2 = of(
|
* { name: 'Ben' },
|
* { name: 'Tracy' },
|
* { name: 'Bradley' },
|
* { name: 'Lincoln' }
|
* );
|
*
|
* source2
|
* .pipe(single(x => x.name.startsWith('B')))
|
* .subscribe({ error: err => console.error(err) });
|
* // Error emitted: SequenceError('Too many values match')
|
*
|
*
|
* const source3 = of(
|
* { name: 'Laney' },
|
* { name: 'Tracy' },
|
* { name: 'Lily' },
|
* { name: 'Lincoln' }
|
* );
|
*
|
* source3
|
* .pipe(single(x => x.name.startsWith('B')))
|
* .subscribe({ error: err => console.error(err) });
|
* // Error emitted: NotFoundError('No values match')
|
* ```
|
*
|
* @see {@link first}
|
* @see {@link find}
|
* @see {@link findIndex}
|
* @see {@link elementAt}
|
*
|
* @throws {NotFoundError} Delivers an NotFoundError to the Observer's `error`
|
* callback if the Observable completes before any `next` notification was sent.
|
* @throws {SequenceError} Delivers a SequenceError if more than one value is emitted that matches the
|
* provided predicate. If no predicate is provided, will deliver a SequenceError if more
|
* than one value comes from the source
|
* @param {Function} predicate - A predicate function to evaluate items emitted by the source Observable.
|
* @return A function that returns an Observable that emits the single item
|
* emitted by the source Observable that matches the predicate.
|
*/
|
export function single<T>(predicate?: (value: T, index: number, source: Observable<T>) => boolean): MonoTypeOperatorFunction<T> {
|
return operate((source, subscriber) => {
|
let hasValue = false;
|
let singleValue: T;
|
let seenValue = false;
|
let index = 0;
|
source.subscribe(
|
createOperatorSubscriber(
|
subscriber,
|
(value) => {
|
seenValue = true;
|
if (!predicate || predicate(value, index++, source)) {
|
hasValue && subscriber.error(new SequenceError('Too many matching values'));
|
hasValue = true;
|
singleValue = value;
|
}
|
},
|
() => {
|
if (hasValue) {
|
subscriber.next(singleValue);
|
subscriber.complete();
|
} else {
|
subscriber.error(seenValue ? new NotFoundError('No matching values') : new EmptyError());
|
}
|
}
|
)
|
);
|
});
|
}
|