Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: sindresorhus/p-filter
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v3.0.0
Choose a base ref
...
head repository: sindresorhus/p-filter
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v4.0.0
Choose a head ref
  • 4 commits
  • 8 files changed
  • 2 contributors

Commits on Jul 8, 2022

  1. Meta tweaks

    sindresorhus committed Jul 8, 2022

    Verified

    This commit was signed with the committer’s verified signature.
    joyeecheung Joyee Cheung
    Copy the full SHA
    048b5ac View commit details

Commits on Dec 22, 2023

  1. Add pFilterIterable (#7)

    noamokman authored Dec 22, 2023

    Verified

    This commit was signed with the committer’s verified signature.
    joyeecheung Joyee Cheung
    Copy the full SHA
    1d093e6 View commit details
  2. Require Node.js 18

    sindresorhus committed Dec 22, 2023
    1
    Copy the full SHA
    1e798c7 View commit details
  3. 4.0.0

    sindresorhus committed Dec 22, 2023

    Verified

    This commit was signed with the committer’s verified signature.
    aduh95 Antoine du Hamel
    Copy the full SHA
    def9865 View commit details
Showing with 192 additions and 21 deletions.
  1. +3 −0 .github/security.md
  2. +4 −3 .github/workflows/main.yml
  3. +45 −1 index.d.ts
  4. +19 −1 index.js
  5. +11 −1 index.test-d.ts
  6. +12 −9 package.json
  7. +61 −3 readme.md
  8. +37 −3 test.js
3 changes: 3 additions & 0 deletions .github/security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Security Policy

To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.
7 changes: 4 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -10,10 +10,11 @@ jobs:
fail-fast: false
matrix:
node-version:
- 16
- 20
- 18
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm install
46 changes: 45 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Options} from 'p-map';
import type {Options} from 'p-map';

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

/**
Filter promises concurrently.
@param input - Iterated over concurrently in the `filterer` function.
@param filterer - The filterer function that decides whether an element should be included into result.
@param options - See the [`p-map` options](https://github.com/sindresorhus/p-map#options).
@returns An async iterable that iterates over the promises in `iterable` and ones returned from `filterer` concurrently, calling `filterer` for each element.
@example
```
import {pFilterIterable} from 'p-filter';
import getWeather from 'get-weather'; // Not a real module
async function * getPlaces() {
const name = await getCapital('Norway');
yield name;
yield 'Bangkok, Thailand';
yield 'Berlin, Germany';
yield 'Tokyo, Japan';
}
const places = getPlaces();
const filterer = async place => {
const weather = await getWeather(place);
return weather.temperature > 30;
};
for await (const element of pFilterIterable(places, filterer)) {
console.log(element);
}
//=> ['Bangkok, Thailand']
```
*/
export function pFilterIterable<ValueType>(
input: Iterable<ValueType | PromiseLike<ValueType>>,
filterer: (
element: ValueType,
index: number
) => boolean | PromiseLike<boolean>,
options?: Options
): AsyncIterable<ValueType>;

export {Options} from 'p-map';
20 changes: 19 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import pMap from 'p-map';
import pMap, {pMapIterable} from 'p-map';

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

return values.filter(value => Boolean(value[0])).map(value => value[1]);
}

export function pFilterIterable(iterable, filterer, options) {
const values = pMapIterable(
iterable,
(element, index) => Promise.all([filterer(element, index), element]),
options,
);

return {
async * [Symbol.asyncIterator]() {
for await (const [value, element] of values) {
if (value) {
yield element;
}
}
},
};
}
12 changes: 11 additions & 1 deletion index.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {expectType} from 'tsd';
import pFilter from './index.js';
import pFilter, {pFilterIterable} from './index.js';

const places = [
'Bangkok, Thailand',
@@ -15,3 +15,13 @@ expectType<Promise<string[]>>(
expectType<Promise<number[]>>(
pFilter(new Set([1, 2]), number => number > 1, {concurrency: 1}),
);

expectType<AsyncIterable<string>>(
pFilterIterable(places, async place =>
place === 'Bangkok, Thailand' ? true : Promise.resolve(false),
),
);

expectType<AsyncIterable<number>>(
pFilterIterable(new Set([1, 2]), number => number > 1, {concurrency: 1}),
);
21 changes: 12 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "p-filter",
"version": "3.0.0",
"version": "4.0.0",
"description": "Filter promises concurrently",
"license": "MIT",
"repository": "sindresorhus/p-filter",
@@ -11,9 +11,13 @@
"url": "https://sindresorhus.com"
},
"type": "module",
"exports": "./index.js",
"exports": {
"types": "./index.d.ts",
"default": "./index.js"
},
"sideEffects": false,
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
"node": ">=18"
},
"scripts": {
"test": "xo && ava && tsd"
@@ -34,15 +38,14 @@
"promises",
"concurrently",
"concurrency",
"parallel",
"bluebird"
"parallel"
],
"dependencies": {
"p-map": "^5.1.0"
"p-map": "^7.0.0"
},
"devDependencies": {
"ava": "^3.15.0",
"tsd": "^0.17.0",
"xo": "^0.44.0"
"ava": "^6.0.1",
"tsd": "^0.30.0",
"xo": "^0.56.0"
}
}
64 changes: 61 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
@@ -6,8 +6,8 @@ Useful when you need to run promise-returning & async functions multiple times w

## Install

```
$ npm install p-filter
```sh
npm install p-filter
```

## Usage
@@ -42,7 +42,7 @@ Returns a `Promise` that is fulfilled when all promises in `input` and ones retu

#### input

Type: `Iterable<Promise|any>`
Type: `Iterable<Promise<unknown> | unknown>`

Iterated over concurrently in the `filterer` function.

@@ -56,6 +56,64 @@ The filterer function that decides whether an element should be included into re

Type: `object`

See the [`p-map` options](https://github.com/sindresorhus/p-map#options).

##### concurrency

Type: `number`\
Default: `Infinity`\
Minimum: `1`

The number of concurrently pending promises returned by `filterer`.

### pFilterIterable(iterable, filterer, options?)

Returns an async iterable that iterates over the promises in `iterable` and ones returned from `filterer` concurrently, calling `filterer` for each element.

```js
import {pFilterIterable} from 'p-filter';
import getWeather from 'get-weather'; // Not a real module

async function * getPlaces() {
const name = await getCapital('Norway');

yield name;
yield 'Bangkok, Thailand';
yield 'Berlin, Germany';
yield 'Tokyo, Japan';
}

const places = getPlaces();

const filterer = async place => {
const weather = await getWeather(place);
return weather.temperature > 30;
};

for await (const element of pFilterIterable(places, filterer)) {
console.log(element);
}
//=> ['Bangkok, Thailand']
```

#### iterable

Type: `Iterable<Promise<unknown> | unknown>`

Iterated over concurrently in the `filterer` function.

#### filterer(element, index)

Type: `Function`

The filterer function that decides whether an element should be included into result. Expected to return `boolean | Promise<boolean>`.

#### options

Type: `object`

See the [`p-map` options](https://github.com/sindresorhus/p-map#options).

##### concurrency

Type: `number`\
40 changes: 37 additions & 3 deletions test.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,46 @@
import test from 'ava';
import pFilter from './index.js';
import pFilter, {pFilterIterable} from './index.js';

// See `p-map` for more comprehensive tests
test('main', async t => {
t.deepEqual(await pFilter([Promise.resolve(1), 2, 3, 4], x => x % 2), [1, 3]);
t.deepEqual(await pFilter([1, 2, 3, 4], x => Promise.resolve(x % 2)), [1, 3]);
t.deepEqual(
await pFilter([Promise.resolve(1), 2, 3, 4], x => x % 2),
[1, 3],
);
t.deepEqual(
await pFilter([1, 2, 3, 4], x => Promise.resolve(x % 2)),
[1, 3],
);
});

test('handles empty iterable', async t => {
t.deepEqual(await pFilter([]), []);
});

test('pFilterIterable', async t => {
const rangeIterable = {
async * [Symbol.asyncIterator]() {
yield 1;
yield 2;
yield 3;
yield 4;
},
};
const iterable = pFilterIterable(rangeIterable, x => x % 2);
const results = [];
for await (const x of iterable) {
results.push(x);
}

t.deepEqual(results, [1, 3]);

const iterable2 = pFilterIterable(rangeIterable, x =>
Promise.resolve(x % 2),
);
const results2 = [];
for await (const x of iterable2) {
results2.push(x);
}

t.deepEqual(results2, [1, 3]);
});