Skip to content

Commit 42d1add

Browse files
Andre Medeirosbenlesh
Andre Medeiros
authored andcommitted
feat(count): add predicate support in count()
Add a predicate function argument (and a thisArg argument) to count() operator. Also add respective tests that mirror RxJS 4 tests. Resolves issue #425.
1 parent e773b5e commit 42d1add

File tree

2 files changed

+145
-9
lines changed

2 files changed

+145
-9
lines changed

Diff for: spec/operators/count-spec.js

+103
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,107 @@ describe('count', function () {
9595

9696
expectObservable(e1.count()).toBe(expected, null, new Error('too bad'));
9797
});
98+
99+
it('should handle an always-true predicate on an empty hot observable', function () {
100+
var e1 = hot('-x-^---|');
101+
var expected = '----(w|)';
102+
var predicate = function () {
103+
return true;
104+
};
105+
106+
expectObservable(e1.count(predicate)).toBe(expected, { w: 0 });
107+
});
108+
109+
it('should handle an always-false predicate on an empty hot observable', function () {
110+
var e1 = hot('-x-^---|');
111+
var expected = '----(w|)';
112+
var predicate = function () {
113+
return false;
114+
};
115+
116+
expectObservable(e1.count(predicate)).toBe(expected, { w: 0 });
117+
});
118+
119+
it('should handle an always-true predicate on a simple hot observable', function () {
120+
var e1 = hot('-x-^-a-|');
121+
var expected = '----(w|)';
122+
var predicate = function () {
123+
return true;
124+
};
125+
126+
expectObservable(e1.count(predicate)).toBe(expected, { w: 1 });
127+
});
128+
129+
it('should handle an always-false predicate on a simple hot observable', function () {
130+
var e1 = hot('-x-^-a-|');
131+
var expected = '----(w|)';
132+
var predicate = function () {
133+
return false;
134+
};
135+
136+
expectObservable(e1.count(predicate)).toBe(expected, { w: 0 });
137+
});
138+
139+
it('should handle a match-all predicate on observable with many values', function () {
140+
var e1 = hot('-1-^-2--3--4-|');
141+
var expected = '----------(w|)';
142+
var predicate = function (value) {
143+
return parseInt(value) < 10;
144+
};
145+
146+
expectObservable(e1.count(predicate)).toBe(expected, { w: 3 });
147+
});
148+
149+
it('should handle a match-none predicate on observable with many values', function () {
150+
var e1 = hot('-1-^-2--3--4-|');
151+
var expected = '----------(w|)';
152+
var predicate = function (value) {
153+
return parseInt(value) > 10;
154+
};
155+
156+
expectObservable(e1.count(predicate)).toBe(expected, { w: 0 });
157+
});
158+
159+
it('should handle an always-true predicate on observable that throws', function () {
160+
var e1 = hot('-1-^---#');
161+
var expected = '----#';
162+
var predicate = function () {
163+
return true;
164+
};
165+
166+
expectObservable(e1.count(predicate)).toBe(expected);
167+
});
168+
169+
it('should handle an always-false predicate on observable that throws', function () {
170+
var e1 = hot('-1-^---#');
171+
var expected = '----#';
172+
var predicate = function () {
173+
return false;
174+
};
175+
176+
expectObservable(e1.count(predicate)).toBe(expected);
177+
});
178+
179+
it('should handle an always-true predicate on a hot never-observable', function () {
180+
var e1 = hot('-x-^----');
181+
var expected = '-----';
182+
var predicate = function () {
183+
return true;
184+
};
185+
186+
expectObservable(e1.count(predicate)).toBe(expected);
187+
});
188+
189+
it('should handle a predicate that throws, on observable with many values', function () {
190+
var e1 = hot('-1-^-2--3--|');
191+
var expected = '-----#';
192+
var predicate = function (value) {
193+
if (value === '3') {
194+
throw 'error';
195+
}
196+
return true;
197+
};
198+
199+
expectObservable(e1.count(predicate)).toBe(expected);
200+
});
98201
});

Diff for: src/operators/count.ts

+42-9
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,60 @@
1+
import Observable from '../Observable';
12
import Operator from '../Operator';
23
import Observer from '../Observer';
34
import Subscriber from '../Subscriber';
45

5-
export default function count() {
6-
return this.lift(new CountOperator());
6+
import tryCatch from '../util/tryCatch';
7+
import {errorObject} from '../util/errorObject';
8+
import bindCallback from '../util/bindCallback';
9+
10+
export default function count<T>(predicate?: (value: T,
11+
index: number,
12+
source: Observable<T>) => boolean,
13+
thisArg?: any): Observable<T> {
14+
return this.lift(new CountOperator(predicate, thisArg, this));
715
}
816

917
class CountOperator<T, R> implements Operator<T, R> {
10-
call(subscriber: Subscriber<number>): Subscriber<T> {
11-
return new CountSubscriber<T>(subscriber);
18+
constructor(private predicate?: (value: T, index: number, source: Observable<T>) => boolean,
19+
private thisArg?: any,
20+
private source?: Observable<T>) {
21+
}
22+
23+
call(subscriber: Subscriber<R>): Subscriber<T> {
24+
return new CountSubscriber<T>(
25+
subscriber, this.predicate, this.thisArg, this.source
26+
);
1227
}
1328
}
1429

1530
class CountSubscriber<T> extends Subscriber<T> {
31+
private predicate: Function;
32+
private count: number = 0;
33+
private index: number = 0;
1634

17-
count: number = 0;
18-
19-
constructor(destination: Subscriber<number>) {
35+
constructor(destination: Observer<T>,
36+
predicate?: (value: T, index: number, source: Observable<T>) => boolean,
37+
private thisArg?: any,
38+
private source?: Observable<T>) {
2039
super(destination);
40+
if (typeof predicate === 'function') {
41+
this.predicate = bindCallback(predicate, thisArg, 3);
42+
}
2143
}
2244

23-
_next(x) {
24-
this.count += 1;
45+
_next(value: T) {
46+
const predicate = this.predicate;
47+
let passed: any = true;
48+
if (predicate) {
49+
passed = tryCatch(predicate)(value, this.index++, this.source);
50+
if (passed === errorObject) {
51+
this.destination.error(passed.e);
52+
return;
53+
}
54+
}
55+
if (passed) {
56+
this.count += 1;
57+
}
2558
}
2659

2760
_complete() {

0 commit comments

Comments
 (0)