Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pMapSkip #39

Merged
merged 9 commits into from Jun 28, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
34 changes: 33 additions & 1 deletion index.d.ts
Expand Up @@ -58,4 +58,36 @@ export default function pMap<Element, NewElement>(
input: Iterable<Element>,
mapper: Mapper<Element, NewElement>,
options?: Options
): Promise<NewElement[]>;
): Promise<Array<Exclude<NewElement, typeof skip>>>;

/**
Return this value from a `mapper` function to skip adding a value in the returned array.

@example
```js
import pMap, {skip} from 'p-map';
import got from 'got';

const sites = [
getWebsiteFromUsername('sindresorhus'), //=> Promise
'https://avajs.dev',
'https://example.invalid',
'https://github.com'
];

const mapper = async site => {
try {
const {requestUrl} = await got.head(site);
return requestUrl;
} catch {
return skip
}
};

const result = await pMap(sites, mapper, {concurrency: 2});

console.log(result);
//=> ['https://sindresorhus.com/', 'https://avajs.dev/', 'https://github.com/']
Richienb marked this conversation as resolved.
Show resolved Hide resolved
```
*/
export const skip: unique symbol;
15 changes: 14 additions & 1 deletion index.js
Expand Up @@ -19,6 +19,7 @@ export default async function pMap(

const result = [];
const errors = [];
const skippedIndexes = [];
const iterator = iterable[Symbol.iterator]();
let isRejected = false;
let isIterableDone = false;
Expand All @@ -41,6 +42,10 @@ export default async function pMap(
if (!stopOnError && errors.length > 0) {
reject(new AggregateError(errors));
} else {
for (const skippedIndex of skippedIndexes) {
result.splice(skippedIndex, 1);
}

resolve(result);
}
}
Expand All @@ -53,7 +58,13 @@ export default async function pMap(
(async () => {
try {
const element = await nextItem.value;
result[index] = await mapper(element, index);
const value = await mapper(element, index);
if (value === skip) {
skippedIndexes.push(index);
} else {
result[index] = value;
}

resolvingCount--;
next();
} catch (error) {
Expand All @@ -78,3 +89,5 @@ export default async function pMap(
}
});
}

export const skip = Symbol('skip');
10 changes: 9 additions & 1 deletion index.test-d.ts
@@ -1,5 +1,5 @@
import {expectType, expectAssignable} from 'tsd';
import pMap, {Options, Mapper} from './index.js';
import pMap, {Options, Mapper, skip} from './index.js';

const sites = [
'https://sindresorhus.com',
Expand Down Expand Up @@ -40,3 +40,11 @@ expectType<Promise<string[]>>(pMap(sites, (site: string) => site));
expectType<Promise<number[]>>(pMap(sites, (site: string) => site.length));

expectType<Promise<number[]>>(pMap(numbers, (number: number) => number * 2));

expectType<Promise<number[]>>(pMap(numbers, (number: number) => {
if (number % 2 === 0) {
return number * 2;
}

return skip;
}));
30 changes: 30 additions & 0 deletions readme.md
Expand Up @@ -72,6 +72,36 @@ Default: `true`

When set to `false`, instead of stopping when a promise rejects, it will wait for all the promises to settle and then reject with an [aggregated error](https://github.com/sindresorhus/aggregate-error) containing all the errors from the rejected promises.

### pMap.skip
Richienb marked this conversation as resolved.
Show resolved Hide resolved

Return this value from a `mapper` function to skip adding a value in the returned array.
Richienb marked this conversation as resolved.
Show resolved Hide resolved

```js
import pMap, {skip} from 'p-map';
import got from 'got';

const sites = [
getWebsiteFromUsername('sindresorhus'), //=> Promise
'https://avajs.dev',
'https://example.invalid',
'https://github.com'
];

const mapper = async site => {
try {
const {requestUrl} = await got.head(site);
return requestUrl;
} catch {
return skip
}
};

const result = await pMap(sites, mapper, {concurrency: 2});

console.log(result);
//=> ['https://sindresorhus.com/', 'https://avajs.dev/', 'https://github.com/']
```

## p-map for enterprise

Available as part of the Tidelift Subscription.
Expand Down
10 changes: 9 additions & 1 deletion test.js
Expand Up @@ -4,7 +4,7 @@ import inRange from 'in-range';
import timeSpan from 'time-span';
import randomInt from 'random-int';
import AggregateError from 'aggregate-error';
import pMap from './index.js';
import pMap, {skip} from './index.js';

const sharedInput = [
Promise.resolve([10, 300]),
Expand Down Expand Up @@ -107,3 +107,11 @@ test('aggregate errors when stopOnError is false', async t => {
await t.throwsAsync(pMap(errorInput1, mapper, {concurrency: 1, stopOnError: false}), {instanceOf: AggregateError, message: /foo(.|\n)*bar/});
await t.throwsAsync(pMap(errorInput2, mapper, {concurrency: 1, stopOnError: false}), {instanceOf: AggregateError, message: /bar(.|\n)*foo/});
});

test('.skip', async t => {
t.deepEqual(await pMap([
1,
skip,
2
], async value => value), [1, 2]);
});