From 3c20fcc2e104ebb0d85bd216750e95132bc543fd Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Tue, 29 Sep 2015 16:15:13 -0700 Subject: [PATCH] feat(first): add resultSelector closes #417 --- spec/operators/first-spec.js | 18 +++++++++++++++--- src/CoreOperators.ts | 2 +- src/operators/first.ts | 32 ++++++++++++++++++++------------ 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/spec/operators/first-spec.js b/spec/operators/first-spec.js index d2ee91bbf9..9a6832b600 100644 --- a/spec/operators/first-spec.js +++ b/spec/operators/first-spec.js @@ -23,7 +23,7 @@ describe('Observable.prototype.first()', function() { it('should return the default value if source observable was empty', function() { var e1 = hot('-----^----|'); var expected = '-----(a|)'; - expectObservable(e1.first(null, null, 'a')).toBe(expected); + expectObservable(e1.first(null, null, null, 'a')).toBe(expected); }); it('should propagate error from the source observable', function() { @@ -63,7 +63,7 @@ describe('Observable.prototype.first()', function() { expect(this).toEqual(42); return value % 2 === 1; }; - expectObservable(e1.first(predicate, 42)).toBe(expected, {c: 3}); + expectObservable(e1.first(predicate, null, 42)).toBe(expected, {c: 3}); }); it('should error when no value matches the predicate', function() { @@ -81,7 +81,7 @@ describe('Observable.prototype.first()', function() { var predicate = function (value) { return value === 's'; }; - expectObservable(e1.first(predicate, null, 'd')).toBe(expected); + expectObservable(e1.first(predicate, null, null, 'd')).toBe(expected); }); it('should propagate error when no value matches the predicate', function() { @@ -114,4 +114,16 @@ describe('Observable.prototype.first()', function() { }; expectObservable(e1.first(predicate)).toBe(expected, null, 'error'); }); + + it('should support a result selector argument', function() { + var e1 = hot('--a--^---b---c---d---e--|'); + var expected = '--------(x|)'; + var predicate = function (x){ return x === 'c'; }; + var resultSelector = function(x, i) { + expect(i).toBe(1); + expect(x).toBe('c'); + return 'x'; + }; + expectObservable(e1.first(predicate, resultSelector)).toBe(expected); + }); }); diff --git a/src/CoreOperators.ts b/src/CoreOperators.ts index 899d94df32..574a1cfc00 100644 --- a/src/CoreOperators.ts +++ b/src/CoreOperators.ts @@ -26,7 +26,7 @@ export interface CoreOperators { expand?: (project: (x: T, ix: number) => Observable) => Observable; filter?: (predicate: (x: T) => boolean, ix?: number, thisArg?: any) => Observable; finally?: (ensure: () => void, thisArg?: any) => Observable; - first?: (predicate?: (value: T, index: number, source: Observable) => boolean, thisArg?: any, defaultValue?: any) => Observable; + first?: (predicate?: (value: T, index: number, source: Observable) => boolean, resultSelector?: (value:T, index: number) => R, thisArg?: any, defaultValue?: any) => Observable; flatMap?: (project: ((x: T, ix: number) => Observable), projectResult?: (x: T, y: any, ix: number, iy: number) => R, concurrent?: number) => Observable; flatMapTo?: (observable: Observable, projectResult?: (x: T, y: any, ix: number, iy: number) => R, concurrent?: number) => Observable; groupBy?: (keySelector: (value:T) => string, durationSelector?: (group:GroupSubject) => Observable, elementSelector?: (value:T) => R) => Observable; diff --git a/src/operators/first.ts b/src/operators/first.ts index b6b8be85fa..b1393a07e1 100644 --- a/src/operators/first.ts +++ b/src/operators/first.ts @@ -8,27 +8,27 @@ import {errorObject} from '../util/errorObject'; import bindCallback from '../util/bindCallback'; import EmptyError from '../util/EmptyError'; -export default function first(predicate?: (value: T, index: number, source: Observable) => boolean, +export default function first(predicate?: (value: T, index: number, source: Observable) => boolean, + resultSelector?: (value: T, index: number) => R, thisArg?: any, - defaultValue?: any): Observable { - return this.lift(new FirstOperator(predicate, thisArg, defaultValue, this)); + defaultValue?: any): Observable { + return this.lift(new FirstOperator(predicate, thisArg, resultSelector, defaultValue, this)); } class FirstOperator implements Operator { constructor(private predicate?: (value: T, index: number, source: Observable) => boolean, private thisArg?: any, + private resultSelector?: (value: T, index: number) => R, private defaultValue?: any, private source?: Observable) { } call(observer: Subscriber): Subscriber { - return new FirstSubscriber( - observer, this.predicate, this.thisArg, this.defaultValue, this.source - ); + return new FirstSubscriber(observer, this.predicate, this.thisArg, this.resultSelector, this.defaultValue, this.source); } } -class FirstSubscriber extends Subscriber { +class FirstSubscriber extends Subscriber { private predicate: Function; private index: number = 0; private hasCompleted: boolean = false; @@ -36,6 +36,7 @@ class FirstSubscriber extends Subscriber { constructor(destination: Observer, predicate?: (value: T, index: number, source: Observable) => boolean, private thisArg?: any, + private resultSelector?: (value: T, index: number) => R, private defaultValue?: any, private source?: Observable) { super(destination); @@ -44,18 +45,25 @@ class FirstSubscriber extends Subscriber { } } - _next(value: T) { - const destination = this.destination; - const predicate = this.predicate; + _next(value: any) { + const { destination, predicate, resultSelector } = this; + const index = this.index++; let passed: any = true; if (predicate) { - passed = tryCatch(predicate)(value, this.index++, this.source); + passed = tryCatch(predicate)(value,index, this.source); if (passed === errorObject) { - destination.error(passed.e); + destination.error(errorObject.e); return; } } if (passed) { + if(resultSelector) { + value = tryCatch(resultSelector)(value, index); + if(value === errorObject) { + destination.error(errorObject.e); + return; + } + } destination.next(value); destination.complete(); this.hasCompleted = true;