Skip to content

Commit b8e956b

Browse files
committed
feat(elementAt): add higher-order lettable version of elementAt
1 parent bcde577 commit b8e956b

File tree

3 files changed

+100
-49
lines changed

3 files changed

+100
-49
lines changed

src/operator/elementAt.ts

Lines changed: 3 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import { Operator } from '../Operator';
2-
import { Subscriber } from '../Subscriber';
3-
import { ArgumentOutOfRangeError } from '../util/ArgumentOutOfRangeError';
1+
42
import { Observable } from '../Observable';
5-
import { TeardownLogic } from '../Subscription';
3+
import { elementAt as higherOrder } from '../operators';
64

75
/**
86
* Emits the single value at the specified `index` in a sequence of emissions
@@ -47,49 +45,5 @@ import { TeardownLogic } from '../Subscription';
4745
* @owner Observable
4846
*/
4947
export function elementAt<T>(this: Observable<T>, index: number, defaultValue?: T): Observable<T> {
50-
return this.lift(new ElementAtOperator(index, defaultValue));
51-
}
52-
53-
class ElementAtOperator<T> implements Operator<T, T> {
54-
55-
constructor(private index: number, private defaultValue?: T) {
56-
if (index < 0) {
57-
throw new ArgumentOutOfRangeError;
58-
}
59-
}
60-
61-
call(subscriber: Subscriber<T>, source: any): TeardownLogic {
62-
return source.subscribe(new ElementAtSubscriber(subscriber, this.index, this.defaultValue));
63-
}
64-
}
65-
66-
/**
67-
* We need this JSDoc comment for affecting ESDoc.
68-
* @ignore
69-
* @extends {Ignored}
70-
*/
71-
class ElementAtSubscriber<T> extends Subscriber<T> {
72-
73-
constructor(destination: Subscriber<T>, private index: number, private defaultValue?: T) {
74-
super(destination);
75-
}
76-
77-
protected _next(x: T) {
78-
if (this.index-- === 0) {
79-
this.destination.next(x);
80-
this.destination.complete();
81-
}
82-
}
83-
84-
protected _complete() {
85-
const destination = this.destination;
86-
if (this.index >= 0) {
87-
if (typeof this.defaultValue !== 'undefined') {
88-
destination.next(this.defaultValue);
89-
} else {
90-
destination.error(new ArgumentOutOfRangeError);
91-
}
92-
}
93-
destination.complete();
94-
}
48+
return higherOrder(index, defaultValue)(this);
9549
}

src/operators/elementAt.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { Operator } from '../Operator';
2+
import { Subscriber } from '../Subscriber';
3+
import { ArgumentOutOfRangeError } from '../util/ArgumentOutOfRangeError';
4+
import { Observable } from '../Observable';
5+
import { TeardownLogic } from '../Subscription';
6+
import { MonoTypeOperatorFunction } from '../interfaces';
7+
8+
/**
9+
* Emits the single value at the specified `index` in a sequence of emissions
10+
* from the source Observable.
11+
*
12+
* <span class="informal">Emits only the i-th value, then completes.</span>
13+
*
14+
* <img src="./img/elementAt.png" width="100%">
15+
*
16+
* `elementAt` returns an Observable that emits the item at the specified
17+
* `index` in the source Observable, or a default value if that `index` is out
18+
* of range and the `default` argument is provided. If the `default` argument is
19+
* not given and the `index` is out of range, the output Observable will emit an
20+
* `ArgumentOutOfRangeError` error.
21+
*
22+
* @example <caption>Emit only the third click event</caption>
23+
* var clicks = Rx.Observable.fromEvent(document, 'click');
24+
* var result = clicks.elementAt(2);
25+
* result.subscribe(x => console.log(x));
26+
*
27+
* // Results in:
28+
* // click 1 = nothing
29+
* // click 2 = nothing
30+
* // click 3 = MouseEvent object logged to console
31+
*
32+
* @see {@link first}
33+
* @see {@link last}
34+
* @see {@link skip}
35+
* @see {@link single}
36+
* @see {@link take}
37+
*
38+
* @throws {ArgumentOutOfRangeError} When using `elementAt(i)`, it delivers an
39+
* ArgumentOutOrRangeError to the Observer's `error` callback if `i < 0` or the
40+
* Observable has completed before emitting the i-th `next` notification.
41+
*
42+
* @param {number} index Is the number `i` for the i-th source emission that has
43+
* happened since the subscription, starting from the number `0`.
44+
* @param {T} [defaultValue] The default value returned for missing indices.
45+
* @return {Observable} An Observable that emits a single item, if it is found.
46+
* Otherwise, will emit the default value if given. If not, then emits an error.
47+
* @method elementAt
48+
* @owner Observable
49+
*/
50+
export function elementAt<T>(index: number, defaultValue?: T): MonoTypeOperatorFunction<T> {
51+
return (source: Observable<T>) => source.lift(new ElementAtOperator(index, defaultValue));
52+
}
53+
54+
class ElementAtOperator<T> implements Operator<T, T> {
55+
56+
constructor(private index: number, private defaultValue?: T) {
57+
if (index < 0) {
58+
throw new ArgumentOutOfRangeError;
59+
}
60+
}
61+
62+
call(subscriber: Subscriber<T>, source: any): TeardownLogic {
63+
return source.subscribe(new ElementAtSubscriber(subscriber, this.index, this.defaultValue));
64+
}
65+
}
66+
67+
/**
68+
* We need this JSDoc comment for affecting ESDoc.
69+
* @ignore
70+
* @extends {Ignored}
71+
*/
72+
class ElementAtSubscriber<T> extends Subscriber<T> {
73+
74+
constructor(destination: Subscriber<T>, private index: number, private defaultValue?: T) {
75+
super(destination);
76+
}
77+
78+
protected _next(x: T) {
79+
if (this.index-- === 0) {
80+
this.destination.next(x);
81+
this.destination.complete();
82+
}
83+
}
84+
85+
protected _complete() {
86+
const destination = this.destination;
87+
if (this.index >= 0) {
88+
if (typeof this.defaultValue !== 'undefined') {
89+
destination.next(this.defaultValue);
90+
} else {
91+
destination.error(new ArgumentOutOfRangeError);
92+
}
93+
}
94+
destination.complete();
95+
}
96+
}

src/operators/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export { delayWhen } from './delayWhen';
1919
export { dematerialize } from './dematerialize';
2020
export { distinctUntilChanged } from './distinctUntilChanged';
2121
export { distinctUntilKeyChanged } from './distinctUntilKeyChanged';
22+
export { elementAt } from './elementAt';
2223
export { filter } from './filter';
2324
export { ignoreElements } from './ignoreElements';
2425
export { map } from './map';

0 commit comments

Comments
 (0)