Skip to content

Commit 1d093e6

Browse files
authoredDec 22, 2023
Add pFilterIterable (#7)
1 parent 048b5ac commit 1d093e6

File tree

6 files changed

+175
-10
lines changed

6 files changed

+175
-10
lines changed
 

‎index.d.ts

+45-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Options} from 'p-map';
1+
import type {Options} from 'p-map';
22

33
/**
44
Filter promises concurrently.
@@ -38,4 +38,48 @@ export default function pFilter<ValueType>(
3838
options?: Options
3939
): Promise<ValueType[]>;
4040

41+
/**
42+
Filter promises concurrently.
43+
44+
@param input - Iterated over concurrently in the `filterer` function.
45+
@param filterer - The filterer function that decides whether an element should be included into result.
46+
@param options - See the [`p-map` options](https://github.com/sindresorhus/p-map#options).
47+
@returns An async iterable that iterates over the promises in `iterable` and ones returned from `filterer` concurrently, calling `filterer` for each element.
48+
49+
@example
50+
```
51+
import {pFilterIterable} from 'p-filter';
52+
import getWeather from 'get-weather'; // Not a real module
53+
54+
async function * getPlaces() {
55+
const name = await getCapital('Norway');
56+
57+
yield name;
58+
yield 'Bangkok, Thailand';
59+
yield 'Berlin, Germany';
60+
yield 'Tokyo, Japan';
61+
}
62+
63+
const places = getPlaces();
64+
65+
const filterer = async place => {
66+
const weather = await getWeather(place);
67+
return weather.temperature > 30;
68+
};
69+
70+
const result = await pFilterIterable(places, filterer);
71+
72+
console.log(result);
73+
//=> ['Bangkok, Thailand']
74+
```
75+
*/
76+
export function pFilterIterable<ValueType>(
77+
input: Iterable<ValueType | PromiseLike<ValueType>>,
78+
filterer: (
79+
element: ValueType,
80+
index: number
81+
) => boolean | PromiseLike<boolean>,
82+
options?: Options
83+
): AsyncIterable<ValueType>;
84+
4185
export {Options} from 'p-map';

‎index.js

+19-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import pMap from 'p-map';
1+
import pMap, {pMapIterable} from 'p-map';
22

33
export default async function pFilter(iterable, filterer, options) {
44
const values = await pMap(
@@ -9,3 +9,21 @@ export default async function pFilter(iterable, filterer, options) {
99

1010
return values.filter(value => Boolean(value[0])).map(value => value[1]);
1111
}
12+
13+
export function pFilterIterable(iterable, filterer, options) {
14+
const values = pMapIterable(
15+
iterable,
16+
(element, index) => Promise.all([filterer(element, index), element]),
17+
options,
18+
);
19+
20+
return {
21+
async * [Symbol.asyncIterator]() {
22+
for await (const [value, element] of values) {
23+
if (value) {
24+
yield element;
25+
}
26+
}
27+
},
28+
};
29+
}

‎index.test-d.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {expectType} from 'tsd';
2-
import pFilter from './index.js';
2+
import pFilter, {pFilterIterable} from './index.js';
33

44
const places = [
55
'Bangkok, Thailand',
@@ -15,3 +15,13 @@ expectType<Promise<string[]>>(
1515
expectType<Promise<number[]>>(
1616
pFilter(new Set([1, 2]), number => number > 1, {concurrency: 1}),
1717
);
18+
19+
expectType<AsyncIterable<string>>(
20+
pFilterIterable(places, async place =>
21+
place === 'Bangkok, Thailand' ? true : Promise.resolve(false),
22+
),
23+
);
24+
25+
expectType<AsyncIterable<number>>(
26+
pFilterIterable(new Set([1, 2]), number => number > 1, {concurrency: 1}),
27+
);

‎package.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@
3838
"bluebird"
3939
],
4040
"dependencies": {
41-
"p-map": "^5.1.0"
41+
"p-map": "^7.0.0"
4242
},
4343
"devDependencies": {
44-
"ava": "^3.15.0",
45-
"tsd": "^0.17.0",
46-
"xo": "^0.44.0"
44+
"ava": "^6.0.1",
45+
"tsd": "^0.30.0",
46+
"xo": "^0.56.0"
4747
}
4848
}

‎readme.md

+59
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,65 @@ The filterer function that decides whether an element should be included into re
5656

5757
Type: `object`
5858

59+
See the [`p-map` options](https://github.com/sindresorhus/p-map#options).
60+
61+
##### concurrency
62+
63+
Type: `number`\
64+
Default: `Infinity`\
65+
Minimum: `1`
66+
67+
The number of concurrently pending promises returned by `filterer`.
68+
69+
### pFilterIterable(iterable, filterer, options?)
70+
71+
Returns an async iterable that iterates over the promises in `iterable` and ones returned from `filterer` concurrently, calling `filterer` for each element.
72+
73+
#### Usage
74+
```js
75+
import {pFilterIterable} from 'p-filter';
76+
import getWeather from 'get-weather'; // Not a real module
77+
78+
async function * getPlaces() {
79+
const name = await getCapital('Norway');
80+
81+
yield name;
82+
yield 'Bangkok, Thailand';
83+
yield 'Berlin, Germany';
84+
yield 'Tokyo, Japan';
85+
}
86+
87+
const places = getPlaces();
88+
89+
const filterer = async place => {
90+
const weather = await getWeather(place);
91+
return weather.temperature > 30;
92+
};
93+
94+
const result = await pFilterIterable(places, filterer);
95+
96+
console.log(result);
97+
//=> ['Bangkok, Thailand']
98+
```
99+
100+
#### iterable
101+
102+
Type: `Iterable<Promise|any>`
103+
104+
Iterated over concurrently in the `filterer` function.
105+
106+
#### filterer(element, index)
107+
108+
Type: `Function`
109+
110+
The filterer function that decides whether an element should be included into result. Expected to return `boolean | Promise<boolean>`.
111+
112+
#### options
113+
114+
Type: `object`
115+
116+
See the [`p-map` options](https://github.com/sindresorhus/p-map#options).
117+
59118
##### concurrency
60119

61120
Type: `number`\

‎test.js

+37-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,46 @@
11
import test from 'ava';
2-
import pFilter from './index.js';
2+
import pFilter, {pFilterIterable} from './index.js';
33

44
// See `p-map` for more comprehensive tests
55
test('main', async t => {
6-
t.deepEqual(await pFilter([Promise.resolve(1), 2, 3, 4], x => x % 2), [1, 3]);
7-
t.deepEqual(await pFilter([1, 2, 3, 4], x => Promise.resolve(x % 2)), [1, 3]);
6+
t.deepEqual(
7+
await pFilter([Promise.resolve(1), 2, 3, 4], x => x % 2),
8+
[1, 3],
9+
);
10+
t.deepEqual(
11+
await pFilter([1, 2, 3, 4], x => Promise.resolve(x % 2)),
12+
[1, 3],
13+
);
814
});
915

1016
test('handles empty iterable', async t => {
1117
t.deepEqual(await pFilter([]), []);
1218
});
19+
20+
test('pFilterIterable', async t => {
21+
const rangeIterable = {
22+
async * [Symbol.asyncIterator]() {
23+
yield 1;
24+
yield 2;
25+
yield 3;
26+
yield 4;
27+
},
28+
};
29+
const iterable = pFilterIterable(rangeIterable, x => x % 2);
30+
const results = [];
31+
for await (const x of iterable) {
32+
results.push(x);
33+
}
34+
35+
t.deepEqual(results, [1, 3]);
36+
37+
const iterable2 = pFilterIterable(rangeIterable, x =>
38+
Promise.resolve(x % 2),
39+
);
40+
const results2 = [];
41+
for await (const x of iterable2) {
42+
results2.push(x);
43+
}
44+
45+
t.deepEqual(results2, [1, 3]);
46+
});

0 commit comments

Comments
 (0)
Please sign in to comment.