From 284855685927baa45e40637b2fc525c1e2d50f6e Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Thu, 15 Jun 2017 09:02:59 -0700 Subject: [PATCH] feat(filter): add higher-order lettable version of filter --- src/operator/filter.ts | 49 ++------------------ src/operators/filter.ts | 100 ++++++++++++++++++++++++++++++++++++++++ src/operators/index.ts | 2 + 3 files changed, 105 insertions(+), 46 deletions(-) create mode 100644 src/operators/filter.ts diff --git a/src/operator/filter.ts b/src/operator/filter.ts index f0fba4c329..67d81acdf5 100644 --- a/src/operator/filter.ts +++ b/src/operator/filter.ts @@ -1,7 +1,6 @@ -import { Operator } from '../Operator'; -import { Subscriber } from '../Subscriber'; + import { Observable } from '../Observable'; -import { TeardownLogic } from '../Subscription'; +import { filter as higherOrderFilter } from '../operators'; /* tslint:disable:max-line-length */ export function filter(this: Observable, @@ -53,47 +52,5 @@ export function filter(this: Observable, */ export function filter(this: Observable, predicate: (value: T, index: number) => boolean, thisArg?: any): Observable { - return this.lift(new FilterOperator(predicate, thisArg)); -} - -class FilterOperator implements Operator { - constructor(private predicate: (value: T, index: number) => boolean, - private thisArg?: any) { - } - - call(subscriber: Subscriber, source: any): TeardownLogic { - return source.subscribe(new FilterSubscriber(subscriber, this.predicate, this.thisArg)); - } -} - -/** - * We need this JSDoc comment for affecting ESDoc. - * @ignore - * @extends {Ignored} - */ -class FilterSubscriber extends Subscriber { - - count: number = 0; - - constructor(destination: Subscriber, - private predicate: (value: T, index: number) => boolean, - private thisArg: any) { - super(destination); - this.predicate = predicate; - } - - // the try catch block below is left specifically for - // optimization and perf reasons. a tryCatcher is not necessary here. - protected _next(value: T) { - let result: any; - try { - result = this.predicate.call(this.thisArg, value, this.count++); - } catch (err) { - this.destination.error(err); - return; - } - if (result) { - this.destination.next(value); - } - } + return higherOrderFilter(predicate, thisArg)(this); } diff --git a/src/operators/filter.ts b/src/operators/filter.ts new file mode 100644 index 0000000000..ae7252d867 --- /dev/null +++ b/src/operators/filter.ts @@ -0,0 +1,100 @@ +import { Operator } from '../Operator'; +import { Subscriber } from '../Subscriber'; +import { Observable } from '../Observable'; +import { TeardownLogic } from '../Subscription'; +import { OperatorFunction } from './OperatorFunction'; + +/* tslint:disable:max-line-length */ +export function filter(predicate: (value: T, index: number) => value is S, + thisArg?: any): OperatorFunction; +export function filter(predicate: (value: T, index: number) => boolean, + thisArg?: any): OperatorFunction; +/* tslint:enable:max-line-length */ + +/** + * Filter items emitted by the source Observable by only emitting those that + * satisfy a specified predicate. + * + * Like + * [Array.prototype.filter()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter), + * it only emits a value from the source if it passes a criterion function. + * + * + * + * Similar to the well-known `Array.prototype.filter` method, this operator + * takes values from the source Observable, passes them through a `predicate` + * function and only emits those values that yielded `true`. + * + * @example Emit only click events whose target was a DIV element + * var clicks = Rx.Observable.fromEvent(document, 'click'); + * var clicksOnDivs = clicks.filter(ev => ev.target.tagName === 'DIV'); + * clicksOnDivs.subscribe(x => console.log(x)); + * + * @see {@link distinct} + * @see {@link distinctUntilChanged} + * @see {@link distinctUntilKeyChanged} + * @see {@link ignoreElements} + * @see {@link partition} + * @see {@link skip} + * + * @param {function(value: T, index: number): boolean} predicate A function that + * evaluates each value emitted by the source Observable. If it returns `true`, + * the value is emitted, if `false` the value is not passed to the output + * Observable. The `index` parameter is the number `i` for the i-th source + * emission that has happened since the subscription, starting from the number + * `0`. + * @param {any} [thisArg] An optional argument to determine the value of `this` + * in the `predicate` function. + * @return {Observable} An Observable of values from the source that were + * allowed by the `predicate` function. + * @method filter + * @owner Observable + */ +export function filter(predicate: (value: T, index: number) => boolean, + thisArg?: any): OperatorFunction { + return function filterOperatorFunction(source: Observable): Observable { + return source.lift(new FilterOperator(predicate, thisArg)); + }; +} + +class FilterOperator implements Operator { + constructor(private predicate: (value: T, index: number) => boolean, + private thisArg?: any) { + } + + call(subscriber: Subscriber, source: any): TeardownLogic { + return source.subscribe(new FilterSubscriber(subscriber, this.predicate, this.thisArg)); + } +} + +/** + * We need this JSDoc comment for affecting ESDoc. + * @ignore + * @extends {Ignored} + */ +class FilterSubscriber extends Subscriber { + + count: number = 0; + + constructor(destination: Subscriber, + private predicate: (value: T, index: number) => boolean, + private thisArg: any) { + super(destination); + this.predicate = predicate; + } + + // the try catch block below is left specifically for + // optimization and perf reasons. a tryCatcher is not necessary here. + protected _next(value: T) { + let result: any; + try { + result = this.predicate.call(this.thisArg, value, this.count++); + } catch (err) { + this.destination.error(err); + return; + } + if (result) { + this.destination.next(value); + } + } +} diff --git a/src/operators/index.ts b/src/operators/index.ts index 42c9823731..1fe0a78d6b 100644 --- a/src/operators/index.ts +++ b/src/operators/index.ts @@ -1,3 +1,5 @@ export { OperatorFunction } from './OperatorFunction'; +export { filter } from './filter'; + export { map } from './map'; export { mergeMap } from './mergeMap';