Skip to content

Commit 3c20fcc

Browse files
committed
feat(first): add resultSelector
closes #417
1 parent 4d973a3 commit 3c20fcc

File tree

3 files changed

+36
-16
lines changed

3 files changed

+36
-16
lines changed

spec/operators/first-spec.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ describe('Observable.prototype.first()', function() {
2323
it('should return the default value if source observable was empty', function() {
2424
var e1 = hot('-----^----|');
2525
var expected = '-----(a|)';
26-
expectObservable(e1.first(null, null, 'a')).toBe(expected);
26+
expectObservable(e1.first(null, null, null, 'a')).toBe(expected);
2727
});
2828

2929
it('should propagate error from the source observable', function() {
@@ -63,7 +63,7 @@ describe('Observable.prototype.first()', function() {
6363
expect(this).toEqual(42);
6464
return value % 2 === 1;
6565
};
66-
expectObservable(e1.first(predicate, 42)).toBe(expected, {c: 3});
66+
expectObservable(e1.first(predicate, null, 42)).toBe(expected, {c: 3});
6767
});
6868

6969
it('should error when no value matches the predicate', function() {
@@ -81,7 +81,7 @@ describe('Observable.prototype.first()', function() {
8181
var predicate = function (value) {
8282
return value === 's';
8383
};
84-
expectObservable(e1.first(predicate, null, 'd')).toBe(expected);
84+
expectObservable(e1.first(predicate, null, null, 'd')).toBe(expected);
8585
});
8686

8787
it('should propagate error when no value matches the predicate', function() {
@@ -114,4 +114,16 @@ describe('Observable.prototype.first()', function() {
114114
};
115115
expectObservable(e1.first(predicate)).toBe(expected, null, 'error');
116116
});
117+
118+
it('should support a result selector argument', function() {
119+
var e1 = hot('--a--^---b---c---d---e--|');
120+
var expected = '--------(x|)';
121+
var predicate = function (x){ return x === 'c'; };
122+
var resultSelector = function(x, i) {
123+
expect(i).toBe(1);
124+
expect(x).toBe('c');
125+
return 'x';
126+
};
127+
expectObservable(e1.first(predicate, resultSelector)).toBe(expected);
128+
});
117129
});

src/CoreOperators.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export interface CoreOperators<T> {
2626
expand?: (project: (x: T, ix: number) => Observable<any>) => Observable<any>;
2727
filter?: (predicate: (x: T) => boolean, ix?: number, thisArg?: any) => Observable<T>;
2828
finally?: (ensure: () => void, thisArg?: any) => Observable<T>;
29-
first?: (predicate?: (value: T, index: number, source: Observable<T>) => boolean, thisArg?: any, defaultValue?: any) => Observable<T>;
29+
first?: <R>(predicate?: (value: T, index: number, source: Observable<T>) => boolean, resultSelector?: (value:T, index: number) => R, thisArg?: any, defaultValue?: any) => Observable<R>;
3030
flatMap?: <R>(project: ((x: T, ix: number) => Observable<any>), projectResult?: (x: T, y: any, ix: number, iy: number) => R, concurrent?: number) => Observable<R>;
3131
flatMapTo?: <R>(observable: Observable<any>, projectResult?: (x: T, y: any, ix: number, iy: number) => R, concurrent?: number) => Observable<R>;
3232
groupBy?: <T, R>(keySelector: (value:T) => string, durationSelector?: (group:GroupSubject<R>) => Observable<any>, elementSelector?: (value:T) => R) => Observable<R>;

src/operators/first.ts

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,35 @@ import {errorObject} from '../util/errorObject';
88
import bindCallback from '../util/bindCallback';
99
import EmptyError from '../util/EmptyError';
1010

11-
export default function first<T>(predicate?: (value: T, index: number, source: Observable<T>) => boolean,
11+
export default function first<T, R>(predicate?: (value: T, index: number, source: Observable<T>) => boolean,
12+
resultSelector?: (value: T, index: number) => R,
1213
thisArg?: any,
13-
defaultValue?: any): Observable<T> {
14-
return this.lift(new FirstOperator(predicate, thisArg, defaultValue, this));
14+
defaultValue?: any): Observable<R> {
15+
return this.lift(new FirstOperator(predicate, thisArg, resultSelector, defaultValue, this));
1516
}
1617

1718
class FirstOperator<T, R> implements Operator<T, R> {
1819
constructor(private predicate?: (value: T, index: number, source: Observable<T>) => boolean,
1920
private thisArg?: any,
21+
private resultSelector?: (value: T, index: number) => R,
2022
private defaultValue?: any,
2123
private source?: Observable<T>) {
2224
}
2325

2426
call(observer: Subscriber<R>): Subscriber<T> {
25-
return new FirstSubscriber(
26-
observer, this.predicate, this.thisArg, this.defaultValue, this.source
27-
);
27+
return new FirstSubscriber(observer, this.predicate, this.thisArg, this.resultSelector, this.defaultValue, this.source);
2828
}
2929
}
3030

31-
class FirstSubscriber<T> extends Subscriber<T> {
31+
class FirstSubscriber<T, R> extends Subscriber<T> {
3232
private predicate: Function;
3333
private index: number = 0;
3434
private hasCompleted: boolean = false;
3535

3636
constructor(destination: Observer<T>,
3737
predicate?: (value: T, index: number, source: Observable<T>) => boolean,
3838
private thisArg?: any,
39+
private resultSelector?: (value: T, index: number) => R,
3940
private defaultValue?: any,
4041
private source?: Observable<T>) {
4142
super(destination);
@@ -44,18 +45,25 @@ class FirstSubscriber<T> extends Subscriber<T> {
4445
}
4546
}
4647

47-
_next(value: T) {
48-
const destination = this.destination;
49-
const predicate = this.predicate;
48+
_next(value: any) {
49+
const { destination, predicate, resultSelector } = this;
50+
const index = this.index++;
5051
let passed: any = true;
5152
if (predicate) {
52-
passed = tryCatch(predicate)(value, this.index++, this.source);
53+
passed = tryCatch(predicate)(value,index, this.source);
5354
if (passed === errorObject) {
54-
destination.error(passed.e);
55+
destination.error(errorObject.e);
5556
return;
5657
}
5758
}
5859
if (passed) {
60+
if(resultSelector) {
61+
value = tryCatch(resultSelector)(value, index);
62+
if(value === errorObject) {
63+
destination.error(errorObject.e);
64+
return;
65+
}
66+
}
5967
destination.next(value);
6068
destination.complete();
6169
this.hasCompleted = true;

0 commit comments

Comments
 (0)