diff --git a/src/operator/count.ts b/src/operator/count.ts index 2f5035dd7b..7a71f276ce 100644 --- a/src/operator/count.ts +++ b/src/operator/count.ts @@ -1,7 +1,5 @@ import { Observable } from '../Observable'; -import { Operator } from '../Operator'; -import { Observer } from '../Observer'; -import { Subscriber } from '../Subscriber'; +import { count as higherOrder } from '../operators'; /** * Counts the number of emissions on the source and emits that number when the @@ -52,59 +50,5 @@ import { Subscriber } from '../Subscriber'; * @owner Observable */ export function count(this: Observable, predicate?: (value: T, index: number, source: Observable) => boolean): Observable { - return this.lift(new CountOperator(predicate, this)); -} - -class CountOperator implements Operator { - constructor(private predicate?: (value: T, index: number, source: Observable) => boolean, - private source?: Observable) { - } - - call(subscriber: Subscriber, source: any): any { - return source.subscribe(new CountSubscriber(subscriber, this.predicate, this.source)); - } -} - -/** - * We need this JSDoc comment for affecting ESDoc. - * @ignore - * @extends {Ignored} - */ -class CountSubscriber extends Subscriber { - private count: number = 0; - private index: number = 0; - - constructor(destination: Observer, - private predicate?: (value: T, index: number, source: Observable) => boolean, - private source?: Observable) { - super(destination); - } - - protected _next(value: T): void { - if (this.predicate) { - this._tryPredicate(value); - } else { - this.count++; - } - } - - private _tryPredicate(value: T) { - let result: any; - - try { - result = this.predicate(value, this.index++, this.source); - } catch (err) { - this.destination.error(err); - return; - } - - if (result) { - this.count++; - } - } - - protected _complete(): void { - this.destination.next(this.count); - this.destination.complete(); - } + return higherOrder(predicate)(this); } diff --git a/src/operators/count.ts b/src/operators/count.ts new file mode 100644 index 0000000000..d2b19e4a8c --- /dev/null +++ b/src/operators/count.ts @@ -0,0 +1,111 @@ +import { Observable } from '../Observable'; +import { Operator } from '../Operator'; +import { Observer } from '../Observer'; +import { Subscriber } from '../Subscriber'; +import { OperatorFunction } from '../interfaces'; + +/** + * Counts the number of emissions on the source and emits that number when the + * source completes. + * + * Tells how many values were emitted, when the source + * completes. + * + * + * + * `count` transforms an Observable that emits values into an Observable that + * emits a single value that represents the number of values emitted by the + * source Observable. If the source Observable terminates with an error, `count` + * will pass this error notification along without emitting a value first. If + * the source Observable does not terminate at all, `count` will neither emit + * a value nor terminate. This operator takes an optional `predicate` function + * as argument, in which case the output emission will represent the number of + * source values that matched `true` with the `predicate`. + * + * @example Counts how many seconds have passed before the first click happened + * var seconds = Rx.Observable.interval(1000); + * var clicks = Rx.Observable.fromEvent(document, 'click'); + * var secondsBeforeClick = seconds.takeUntil(clicks); + * var result = secondsBeforeClick.count(); + * result.subscribe(x => console.log(x)); + * + * @example Counts how many odd numbers are there between 1 and 7 + * var numbers = Rx.Observable.range(1, 7); + * var result = numbers.count(i => i % 2 === 1); + * result.subscribe(x => console.log(x)); + * + * // Results in: + * // 4 + * + * @see {@link max} + * @see {@link min} + * @see {@link reduce} + * + * @param {function(value: T, i: number, source: Observable): boolean} [predicate] A + * boolean function to select what values are to be counted. It is provided with + * arguments of: + * - `value`: the value from the source Observable. + * - `index`: the (zero-based) "index" of the value from the source Observable. + * - `source`: the source Observable instance itself. + * @return {Observable} An Observable of one number that represents the count as + * described above. + * @method count + * @owner Observable + */ +export function count(predicate?: (value: T, index: number, source: Observable) => boolean): OperatorFunction { + return (source: Observable) => source.lift(new CountOperator(predicate, source)); +} + +class CountOperator implements Operator { + constructor(private predicate?: (value: T, index: number, source: Observable) => boolean, + private source?: Observable) { + } + + call(subscriber: Subscriber, source: any): any { + return source.subscribe(new CountSubscriber(subscriber, this.predicate, this.source)); + } +} + +/** + * We need this JSDoc comment for affecting ESDoc. + * @ignore + * @extends {Ignored} + */ +class CountSubscriber extends Subscriber { + private count: number = 0; + private index: number = 0; + + constructor(destination: Observer, + private predicate?: (value: T, index: number, source: Observable) => boolean, + private source?: Observable) { + super(destination); + } + + protected _next(value: T): void { + if (this.predicate) { + this._tryPredicate(value); + } else { + this.count++; + } + } + + private _tryPredicate(value: T) { + let result: any; + + try { + result = this.predicate(value, this.index++, this.source); + } catch (err) { + this.destination.error(err); + return; + } + + if (result) { + this.count++; + } + } + + protected _complete(): void { + this.destination.next(this.count); + this.destination.complete(); + } +} diff --git a/src/operators/index.ts b/src/operators/index.ts index 9135e84224..51ce43c1cb 100644 --- a/src/operators/index.ts +++ b/src/operators/index.ts @@ -10,6 +10,7 @@ export { concat } from './concat'; export { concatAll } from './concatAll'; export { concatMap } from './concatMap'; export { concatMapTo } from './concatMapTo'; +export { count } from './count'; export { defaultIfEmpty } from './defaultIfEmpty'; export { dematerialize } from './dematerialize'; export { filter } from './filter';