From ef890a8dafe52eef6c918e14e2472d90ec0c1415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Gabriel=20Lima?= Date: Tue, 8 Dec 2015 20:02:07 -0300 Subject: [PATCH] feat(reduce): add thisArg parameter Adds thisArg as third argument to have symmetry with native Array.prototype.reduce(). Relates to #878 and #996. --- spec/observables/ScalarObservable-spec.js | 9 +++++++++ spec/operators/reduce-spec.js | 17 +++++++++++++++++ src/CoreOperators.ts | 2 +- src/Observable.ts | 2 +- src/observable/ScalarObservable.ts | 4 ++-- src/operator/reduce-support.ts | 15 +++++++++------ src/operator/reduce.ts | 4 ++-- 7 files changed, 41 insertions(+), 12 deletions(-) diff --git a/spec/observables/ScalarObservable-spec.js b/spec/observables/ScalarObservable-spec.js index 923fb0432a..9e466b25a9 100644 --- a/spec/observables/ScalarObservable-spec.js +++ b/spec/observables/ScalarObservable-spec.js @@ -135,6 +135,15 @@ describe('ScalarObservable', function () { expect(r).toBe(s); }); + it('should reduce with custom thisArg', function () { + var s = new ScalarObservable(1); + var reducer = { add: function (a, b) { return a + b; } }; + var reduceFunction = function (a, x) { return this.add(a, x); }; + var r = s.reduce(reduceFunction, 1, reducer); + expect(r instanceof ScalarObservable).toBe(true); + expect(r.value).toBe(2); + }); + it('should return ErrorObservable if projection throws', function () { var s = new ScalarObservable(1); var r = s.reduce(function (a, x) { throw 'error'; }, 1); diff --git a/spec/operators/reduce-spec.js b/spec/operators/reduce-spec.js index 286a8fb8a7..dc73ebf890 100644 --- a/spec/operators/reduce-spec.js +++ b/spec/operators/reduce-spec.js @@ -30,6 +30,23 @@ describe('Observable.prototype.reduce()', function () { expectSubscriptions(e1.subscriptions).toBe(e1subs); }); + it('should reduce with custom thisArg', function () { + var e1 = hot('--a--b--c--|'); + var e1subs = '^ !'; + var expected = '-----------(x|)'; + + var reducer = { + add: function (a, b) { return a + b; } + }; + var reduceFunction = function (o, x) { + return this.add(o, x); + }; + var r = e1.reduce(reduceFunction, '', reducer); + + expectObservable(r).toBe(expected, {x: 'abc'}); + expectSubscriptions(e1.subscriptions).toBe(e1subs); + }); + it('should reduce with seed if source is empty', function () { var e1 = hot('--a--^-------|'); var e1subs = '^ !'; diff --git a/src/CoreOperators.ts b/src/CoreOperators.ts index 60e595de1a..9d9d596eed 100644 --- a/src/CoreOperators.ts +++ b/src/CoreOperators.ts @@ -58,7 +58,7 @@ export interface CoreOperators { publishBehavior?: (value: any) => ConnectableObservable; publishReplay?: (bufferSize?: number, windowTime?: number, scheduler?: Scheduler) => ConnectableObservable; publishLast?: () => ConnectableObservable; - reduce?: (project: (acc: R, x: T) => R, seed?: R) => Observable; + reduce?: (project: (acc: R, x: T) => R, seed?: R, thisArg?: any) => Observable; repeat?: (count?: number) => Observable; retry?: (count?: number) => Observable; retryWhen?: (notifier: (errors: Observable) => Observable) => Observable; diff --git a/src/Observable.ts b/src/Observable.ts index 2a2cd95cd0..f6aad2b016 100644 --- a/src/Observable.ts +++ b/src/Observable.ts @@ -240,7 +240,7 @@ export class Observable implements CoreOperators { publishBehavior: (value: any) => ConnectableObservable; publishReplay: (bufferSize?: number, windowTime?: number, scheduler?: Scheduler) => ConnectableObservable; publishLast: () => ConnectableObservable; - reduce: (project: (acc: R, x: T) => R, seed?: R) => Observable; + reduce: (project: (acc: R, x: T) => R, seed?: R, thisArg?: any) => Observable; repeat: (count?: number) => Observable; retry: (count?: number) => Observable; retryWhen: (notifier: (errors: Observable) => Observable) => Observable; diff --git a/src/observable/ScalarObservable.ts b/src/observable/ScalarObservable.ts index 779977851c..12593b39e4 100644 --- a/src/observable/ScalarObservable.ts +++ b/src/observable/ScalarObservable.ts @@ -75,11 +75,11 @@ proto.filter = function (select: (x: T, ix?: number) => boolean, thisArg?: an } }; -proto.reduce = function (project: (acc: R, x: T) => R, seed?: R): Observable { +proto.reduce = function (project: (acc: R, x: T) => R, seed?: R, thisArg?: any): Observable { if (typeof seed === 'undefined') { return this; } - let result = tryCatch(project)(seed, this.value); + let result = tryCatch(project).call(thisArg || this, seed, this.value); if (result === errorObject) { return new ErrorObservable(errorObject.e); } else { diff --git a/src/operator/reduce-support.ts b/src/operator/reduce-support.ts index 4654c39113..2789b7be8e 100644 --- a/src/operator/reduce-support.ts +++ b/src/operator/reduce-support.ts @@ -5,11 +5,13 @@ import {errorObject} from '../util/errorObject'; export class ReduceOperator implements Operator { - constructor(private project: (acc: R, x: T) => R, private seed?: R) { + constructor(private project: (acc: R, x: T) => R, + private seed?: R, + private thisArg?: any) { } call(subscriber: Subscriber): Subscriber { - return new ReduceSubscriber(subscriber, this.project, this.seed); + return new ReduceSubscriber(subscriber, this.project, this.seed, this.thisArg); } } @@ -18,18 +20,19 @@ export class ReduceSubscriber extends Subscriber { acc: R; hasSeed: boolean; hasValue: boolean = false; - project: (acc: R, x: T) => R; - constructor(destination: Subscriber, project: (acc: R, x: T) => R, seed?: R) { + constructor(destination: Subscriber, + private project: (acc: R, x: T) => R, + seed?: R, + private thisArg?: any) { super(destination); this.acc = seed; - this.project = project; this.hasSeed = typeof seed !== 'undefined'; } _next(x) { if (this.hasValue || (this.hasValue = this.hasSeed)) { - const result = tryCatch(this.project).call(this, this.acc, x); + const result = tryCatch(this.project).call(this.thisArg || this, this.acc, x); if (result === errorObject) { this.destination.error(errorObject.e); } else { diff --git a/src/operator/reduce.ts b/src/operator/reduce.ts index 52070f61ae..8c14a879c3 100644 --- a/src/operator/reduce.ts +++ b/src/operator/reduce.ts @@ -1,6 +1,6 @@ import {Observable} from '../Observable'; import {ReduceOperator} from './reduce-support'; -export function reduce(project: (acc: R, x: T) => R, seed?: R): Observable { - return this.lift(new ReduceOperator(project, seed)); +export function reduce(project: (acc: R, x: T) => R, seed?: R, thisArg?: any): Observable { + return this.lift(new ReduceOperator(project, seed, thisArg)); }