Skip to content

Commit

Permalink
feat(operator): Add expand operator.
Browse files Browse the repository at this point in the history
  • Loading branch information
trxcllnt committed Aug 14, 2015
1 parent 526e4c9 commit 47b178b
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 0 deletions.
29 changes: 29 additions & 0 deletions spec/operators/expand-spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
var Rx = require('../../dist/cjs/Rx');
var Observable = Rx.Observable;

describe('Observable.prototype.expand()', function () {
it('should map and recursively flatten', function (done) {
var expected = [1, 2, 3, 4, 5];
Observable.of(0).expand(function (x) {
if (x > 4) {
return Observable.empty();
}
return Observable.of(x + 1);
})
.subscribe(function (x) {
expect(x).toBe(expected.shift());
}, null, done);
});
it('should map and recursively flatten with ScalarObservables', function (done) {
var expected = [1, 2, 3, 4, 5];
Observable.return(0).expand(function (x) {
if (x > 4) {
return Observable.empty();
}
return Observable.return(x + 1);
})
.subscribe(function (x) {
expect(x).toBe(expected.shift());
}, null, done);
});
});
2 changes: 2 additions & 0 deletions src/Observable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ export default class Observable<T> {
projectResult?: (x: T, y: any, ix: number, iy: number) => R,
concurrent?: number) => Observable<R>;

expand: (project: (x: T, ix: number) => Observable<any>) => Observable<any>;

switchAll: <R>() => Observable<R>;
switchLatest: <R>(project: ((x: T, ix: number) => Observable<any>),
projectResult?: (x: T, y: any, ix: number, iy: number) => R) => Observable<R>;
Expand Down
2 changes: 2 additions & 0 deletions src/Rx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import flatMapTo from './operators/flatMapTo';
import switchAll from './operators/switchAll';
import switchLatest from './operators/switchLatest';
import switchLatestTo from './operators/switchLatestTo';
import expand from './operators/expand';

Observable.merge = merge;
observableProto.merge = merge;
Expand All @@ -72,6 +73,7 @@ observableProto.flatMapTo = flatMapTo;
observableProto.switchAll = switchAll;
observableProto.switchLatest = switchLatest;
observableProto.switchLatestTo = switchLatestTo;
observableProto.expand = expand;

import map from './operators/map';
import mapTo from './operators/mapTo';
Expand Down
71 changes: 71 additions & 0 deletions src/operators/expand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import Operator from '../Operator';
import Observer from '../Observer';
import Observable from '../Observable';
import Subscriber from '../Subscriber';

import {MergeSubscriber, MergeInnerSubscriber} from './merge';
import EmptyObservable from '../observables/EmptyObservable';
import ScalarObservable from '../observables/ScalarObservable';

import tryCatch from '../util/tryCatch';
import {errorObject} from '../util/errorObject';

export default function expand<T>(project: (x: T, ix: number) => Observable<any>): Observable<any> {
return this.lift(new ExpandOperator(project));
}

export class ExpandOperator<T, R> extends Operator<T, R> {

project: (x: T, ix: number) => Observable<any>;

constructor(project: (x: T, ix: number) => Observable<any>) {
super();
this.project = project;
}

call(observer: Observer<R>): Observer<T> {
return new ExpandSubscriber(observer, this.project);
}
}

export class ExpandSubscriber<T, R> extends MergeSubscriber<T, R> {

project: (x: T, ix: number) => Observable<any>;

constructor(destination: Observer<R>,
project: (x: T, ix: number) => Observable<any>) {
super(destination, Number.POSITIVE_INFINITY);
this.project = project;
}

_project(value, index) {
const observable = tryCatch(this.project).call(this, value, index);
if (observable === errorObject) {
this.error(errorObject.e);
return null;
}
return observable;
}

_subscribeInner(observable, value, index) {
if(observable instanceof ScalarObservable) {
this.destination.next((<ScalarObservable<T>> observable).value);
this._innerComplete();
this._next((<ScalarObservable<T>> observable).value);
} else if(observable instanceof EmptyObservable) {
this._innerComplete();
} else {
return observable.subscribe(new ExpandInnerSubscriber(this));
}
}
}

export class ExpandInnerSubscriber<T, R> extends MergeInnerSubscriber<T, R> {
constructor(parent: ExpandSubscriber<T, R>) {
super(parent);
}
_next(value) {
this.destination.next(value);
this.parent.next(value);
}
}

0 comments on commit 47b178b

Please sign in to comment.