Skip to content

Commit 9047a7b

Browse files
committed
Add filter, filterStrict utils
1 parent 81c0dc6 commit 9047a7b

File tree

7 files changed

+205
-0
lines changed

7 files changed

+205
-0
lines changed

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ A collection of array-related async utilities.
1616
### Table of contents
1717

1818
* [`asyncEvery()`](#asyncEvery)
19+
* [`asyncFilter()`](#asyncFilter)
20+
* [`asyncFilterStrict()`](#asyncFilterStrict)
1921
* [`asyncForEach()`](#asyncForEach)
2022
* [`asyncForEachStrict()`](#asyncForEachStrict)
2123
* [`asyncMap()`](#asyncMap)
@@ -36,6 +38,40 @@ import { asyncEvery } from '@wojtekmaj/async-array-utils';
3638
const largerThanZero = await asyncEvery([1, 2, 3], async (el) => el > 0); // true
3739
```
3840

41+
### `asyncFilter()`
42+
43+
Creates a new array with all elements that pass the test implemented by the provided asynchronous function.
44+
45+
Note: For optimization purposes, all iterations are ran concurrently. If you rely on any side effects, consider `asyncFilterStrict()` instead.
46+
47+
#### Sample usage
48+
49+
```js
50+
import { asyncFilter } from '@wojtekmaj/async-array-utils';
51+
52+
const asyncFilteredArr = await asyncFilter([1, 2, 3], async (el) => el > 1); // [2, 3]
53+
```
54+
55+
### `asyncFilterStrict()`
56+
57+
Like `asyncFilter()`, but runs iterations non-concurrently.
58+
59+
#### Sample usage
60+
61+
```js
62+
import { asyncFilterStrict } from '@wojtekmaj/async-array-utils';
63+
64+
const indexes = [];
65+
await asyncFilterStrict(
66+
[1, 2, 3],
67+
async (el, index) => {
68+
indexes.push(index);
69+
return el > 1;
70+
},
71+
); // [2, 3]
72+
console.log(indexes); // [0, 1, 2]
73+
```
74+
3975
### `asyncForEach()`
4076

4177
Executes a provided asynchronous function once for each array element.

src/filter.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import asyncForEach from './forEach';
2+
3+
export default function asyncSome(arr, fn) {
4+
const result = [];
5+
6+
// eslint-disable-next-line no-shadow
7+
return asyncForEach(arr, async (cur, idx, arr) => {
8+
const cond = await fn(cur, idx, arr);
9+
10+
if (cond) {
11+
result[idx] = cur;
12+
}
13+
})
14+
.then(() => result.filter((cur, idx) => idx in result));
15+
}

src/filter.spec.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import asyncFilter from './filter';
2+
3+
import {
4+
inputArr,
5+
largerThanTwo,
6+
largerThanTwoInRandomTime,
7+
} from '../test-utils';
8+
9+
describe('asyncFilter()', () => {
10+
it.skip('assertions below are valid for synchronous .filter()', () => {
11+
const filter = jest.fn().mockImplementation(largerThanTwo);
12+
13+
inputArr.filter(filter);
14+
15+
expect.assertions(1 + inputArr.length);
16+
17+
expect(filter).toHaveBeenCalledTimes(inputArr.length);
18+
inputArr.forEach((el, idx) => {
19+
expect(filter).toHaveBeenCalledWith(el, idx, inputArr);
20+
});
21+
});
22+
23+
it('iterates over values properly', async () => {
24+
const filter = jest.fn().mockImplementation(largerThanTwoInRandomTime);
25+
26+
await asyncFilter(inputArr, filter);
27+
28+
expect.assertions(1 + inputArr.length);
29+
30+
expect(filter).toHaveBeenCalledTimes(inputArr.length);
31+
inputArr.forEach((el, idx) => {
32+
expect(filter).toHaveBeenCalledWith(el, idx, inputArr);
33+
});
34+
});
35+
36+
it.skip('assertions below are valid for synchronous .filter()', () => {
37+
const filter = jest.fn().mockImplementation(largerThanTwo);
38+
39+
const result = inputArr.filter(filter);
40+
41+
expect(result).toEqual([3, 4, 5, 6, 7, 8, 9, 10]);
42+
});
43+
44+
it('filters array properly', async () => {
45+
const filter = jest.fn().mockImplementation(largerThanTwoInRandomTime);
46+
47+
const result = await asyncFilter(inputArr, filter);
48+
49+
expect(result).toEqual([3, 4, 5, 6, 7, 8, 9, 10]);
50+
});
51+
});

src/filter_strict.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import asyncForEachStrict from './forEach_strict';
2+
3+
export default function asyncFilterStrict(arr, fn) {
4+
const result = [];
5+
6+
// eslint-disable-next-line no-shadow
7+
return asyncForEachStrict(arr, async (cur, idx, arr) => {
8+
const cond = await fn(cur, idx, arr);
9+
10+
if (cond) {
11+
result[idx] = cur;
12+
}
13+
})
14+
.then(() => result.filter((cur, idx) => idx in result));
15+
}

src/filter_strict.spec.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import asyncFilterStrict from './filter_strict';
2+
3+
import {
4+
doubleInputArr,
5+
inputArr,
6+
largerThanTwo,
7+
largerThanTwoInRandomTime,
8+
makePushDuplicate,
9+
makePushDuplicateInRandomTime,
10+
} from '../test-utils';
11+
12+
describe('asyncFilterStrict()', () => {
13+
it.skip('assertions below are valid for synchronous .filter()', () => {
14+
const filter = jest.fn().mockImplementation(largerThanTwo);
15+
16+
inputArr.filter(filter);
17+
18+
expect.assertions(1 + inputArr.length);
19+
20+
expect(filter).toHaveBeenCalledTimes(inputArr.length);
21+
inputArr.forEach((el, idx) => {
22+
expect(filter).toHaveBeenCalledWith(el, idx, inputArr);
23+
});
24+
});
25+
26+
it('iterates over values properly', async () => {
27+
const filter = jest.fn().mockImplementation(largerThanTwoInRandomTime);
28+
29+
await asyncFilterStrict(inputArr, filter);
30+
31+
expect.assertions(1 + inputArr.length);
32+
33+
expect(filter).toHaveBeenCalledTimes(inputArr.length);
34+
inputArr.forEach((el, idx) => {
35+
expect(filter).toHaveBeenCalledWith(el, idx, inputArr);
36+
});
37+
});
38+
39+
it.skip('assertions below are valid for synchronous .map()', () => {
40+
const [arr, pushDuplicate] = makePushDuplicate();
41+
const mapper = jest.fn().mockImplementation(pushDuplicate);
42+
43+
inputArr.filter(mapper);
44+
45+
expect(mapper).toHaveBeenCalledTimes(inputArr.length);
46+
expect(arr).toEqual(doubleInputArr);
47+
});
48+
49+
it('iterates through an array properly with side effects', async () => {
50+
const [arr, pushDuplicate] = makePushDuplicateInRandomTime();
51+
const mapper = jest.fn().mockImplementation(pushDuplicate);
52+
53+
await asyncFilterStrict(inputArr, mapper);
54+
55+
expect(mapper).toHaveBeenCalledTimes(inputArr.length);
56+
expect(arr).toEqual(doubleInputArr);
57+
});
58+
59+
it.skip('assertions below are valid for synchronous .filter()', () => {
60+
const filter = jest.fn().mockImplementation(largerThanTwo);
61+
62+
const result = inputArr.filter(filter);
63+
64+
expect(result).toEqual([3, 4, 5, 6, 7, 8, 9, 10]);
65+
});
66+
67+
it('filters array properly', async () => {
68+
const filter = jest.fn().mockImplementation(largerThanTwoInRandomTime);
69+
70+
const result = await asyncFilterStrict(inputArr, filter);
71+
72+
expect(result).toEqual([3, 4, 5, 6, 7, 8, 9, 10]);
73+
});
74+
});

src/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import asyncFilter from './filter';
2+
import asyncFilterStrict from './filter_strict';
13
import asyncForEach from './forEach';
24
import asyncForEachStrict from './forEach_strict';
35
import asyncEvery from './every';
@@ -8,6 +10,8 @@ import asyncSome from './some';
810
import asyncSomeStrict from './some_strict';
911

1012
export {
13+
asyncFilter,
14+
asyncFilterStrict,
1115
asyncForEach,
1216
asyncForEachStrict,
1317
asyncEvery,

src/index.spec.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import {
2+
asyncFilter,
3+
asyncFilterStrict,
24
asyncForEach,
35
asyncForEachStrict,
46
asyncEvery,
@@ -10,6 +12,14 @@ import {
1012
} from './index';
1113

1214
describe('index', () => {
15+
it('has asyncFilter exported properly', () => {
16+
expect(asyncFilter).toBeInstanceOf(Function);
17+
});
18+
19+
it('has asyncFilterStrict exported properly', () => {
20+
expect(asyncFilterStrict).toBeInstanceOf(Function);
21+
});
22+
1323
it('has asyncForEach exported properly', () => {
1424
expect(asyncForEach).toBeInstanceOf(Function);
1525
});

0 commit comments

Comments
 (0)