Skip to content

Commit cd562c4

Browse files
kwonojbenlesh
authored andcommitted
feat(operator): add elementAt operator
- adds ArgumentOutOfRangeError error type - adds elementAt operator
1 parent d65e7ea commit cd562c4

File tree

6 files changed

+125
-2
lines changed

6 files changed

+125
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
var RxOld = require("rx");
2+
var RxNew = require("../../../../index");
3+
4+
module.exports = function (suite) {
5+
6+
var oldElementAtWithImmediateScheduler = RxOld.Observable.range(0, 25, RxOld.Scheduler.immediate).elementAt(5);
7+
var newElementAtWithImmediateScheduler = RxNew.Observable.range(0, 25).elementAt(5);
8+
9+
return suite
10+
.add('old elementAt with immediate scheduler', function () {
11+
oldElementAtWithImmediateScheduler.subscribe(_next, _error, _complete);
12+
})
13+
.add('new elementAt with immediate scheduler', function () {
14+
newElementAtWithImmediateScheduler.subscribe(_next, _error, _complete);
15+
});
16+
17+
function _next(x) { }
18+
function _error(e){ }
19+
function _complete(){ }
20+
};

spec/operators/elementat-spec.js

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/* globals describe, it, expect, hot, cold, expectObservable */
2+
var Rx = require('../../dist/cjs/Rx');
3+
var Observable = Rx.Observable;
4+
5+
describe('Observable.prototype.elementAt', function() {
6+
it("should return first element by zero-based index", function() {
7+
var source = hot('--a--b--c--|');
8+
var expected = '--(a|)';
9+
10+
expectObservable(source.elementAt(0)).toBe(expected);
11+
});
12+
13+
it("should return non-first element by zero-based index", function() {
14+
var source = hot('--a--b--c--d--e--f--|');
15+
var expected = '-----------(d|)';
16+
17+
expectObservable(source.elementAt(3)).toBe(expected);
18+
});
19+
20+
it("should return last element by zero-based index", function() {
21+
var source = hot('--a--b--c--|');
22+
var expected = '--------(c|)';
23+
24+
expectObservable(source.elementAt(2)).toBe(expected);
25+
});
26+
27+
it("should throw if index is smaller than zero", function() {
28+
expect(function() { Observable.range(0,10).elementAt(-1); })
29+
.toThrow(new Rx.ArgumentOutOfRangeError);
30+
});
31+
32+
it("should raise error if index is out of range but does not have default value", function() {
33+
var source = hot('--a--|');
34+
var expected = '-----#';
35+
36+
expectObservable(source.elementAt(3))
37+
.toBe(expected, null, new Rx.ArgumentOutOfRangeError);
38+
});
39+
40+
it("should return default value if index is out of range", function() {
41+
var source = hot('--a--|');
42+
var expected = '-----(x|)';
43+
var defaultValue = '42';
44+
45+
expectObservable(source.elementAt(3, defaultValue)).toBe(expected, { x: defaultValue });
46+
});
47+
});

src/Observable.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ export default class Observable<T> {
184184
startWith: <T>(x: T) => Observable<T>;
185185
debounce: <R>(dueTime: number, scheduler?: Scheduler) => Observable<R>;
186186

187+
elementAt: (index: number, defaultValue?: any) => Observable<T>;
187188
last: (predicate?: (value: T, index:number) => boolean, thisArg?: any, defaultValue?: any) => Observable<T>;
188189

189190
filter: (predicate: (x: T) => boolean, ix?: number, thisArg?: any) => Observable<T>;
@@ -233,4 +234,4 @@ export default class Observable<T> {
233234
finally: (ensure: () => void, thisArg?: any) => Observable<T>;
234235
timeout: <T>(due: number|Date, errorToSend?: any, scheduler?: Scheduler) => Observable<T>;
235236
timeoutWith: <T>(due: number|Date, withObservable: Observable<any>, scheduler?: Scheduler) => Observable<T>;
236-
}
237+
}

src/Rx.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import Subscriber from './Subscriber';
1010
import Subscription from './Subscription';
1111
import Notification from './Notification';
1212
import EmptyError from './util/EmptyError';
13+
import ArgumentOutOfRangeError from './util/ArgumentOutOfRangeError';
1314

1415
import ReplaySubject from './subjects/ReplaySubject';
1516
import BehaviorSubject from './subjects/BehaviorSubject';
@@ -102,6 +103,7 @@ import take from './operators/take';
102103
import skip from './operators/skip';
103104
import skipUntil from './operators/skipUntil';
104105
import takeUntil from './operators/takeUntil';
106+
import elementAt from './operators/elementAt';
105107
import filter from './operators/filter';
106108
import distinctUntilChanged from './operators/distinctUntilChanged';
107109
import distinctUntilKeyChanged from './operators/distinctUntilKeyChanged';
@@ -110,6 +112,7 @@ observableProto.take = take;
110112
observableProto.skip = skip;
111113
observableProto.takeUntil = takeUntil;
112114
observableProto.skipUntil = skipUntil;
115+
observableProto.elementAt = elementAt;
113116
observableProto.filter = filter;
114117
observableProto.distinctUntilChanged = distinctUntilChanged;
115118
observableProto.distinctUntilKeyChanged = distinctUntilKeyChanged;
@@ -237,5 +240,6 @@ export {
237240
Notification,
238241
VirtualTimeScheduler,
239242
TestScheduler,
240-
EmptyError
243+
EmptyError,
244+
ArgumentOutOfRangeError
241245
};

src/operators/elementAt.ts

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import Operator from '../Operator';
2+
import Observer from '../Observer';
3+
import Subscriber from '../Subscriber';
4+
import ArgumentOutOfRangeError from '../util/ArgumentOutOfRangeError';
5+
6+
export default function elementAt(index: number, defaultValue?: any) {
7+
return this.lift(new ElementAtOperator(index, defaultValue));
8+
}
9+
10+
class ElementAtOperator<T, R> implements Operator<T,R> {
11+
12+
constructor(private index: number, private defaultValue?: any) {
13+
if (index < 0) {
14+
throw new ArgumentOutOfRangeError;
15+
}
16+
}
17+
18+
call(subscriber: Subscriber<T>): Subscriber<T> {
19+
return new ElementAtSubscriber(subscriber, this.index, this.defaultValue);
20+
}
21+
}
22+
23+
class ElementAtSubscriber<T, R> extends Subscriber<T> {
24+
25+
constructor(destination: Subscriber<T>, private index: number, private defaultValue?: any) {
26+
super(destination);
27+
}
28+
29+
_next(x) {
30+
if (this.index-- === 0) {
31+
this.destination.next(x);
32+
this.destination.complete();
33+
}
34+
}
35+
36+
_complete() {
37+
const destination = this.destination;
38+
if (this.index >= 0) {
39+
if(typeof this.defaultValue !== 'undefined') {
40+
destination.next(this.defaultValue);
41+
} else {
42+
destination.error(new ArgumentOutOfRangeError);
43+
}
44+
}
45+
destination.complete();
46+
}
47+
}

src/util/ArgumentOutOfRangeError.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export default class ArgumentOutOfRangeError implements Error {
2+
name = 'ArgumentOutOfRangeError';
3+
message = 'argument out of range';
4+
}

0 commit comments

Comments
 (0)