From 9047a7b5e286de10b938649e58da70d045f998fb Mon Sep 17 00:00:00 2001 From: Wojciech Maj Date: Thu, 19 Nov 2020 09:43:46 +0100 Subject: [PATCH] Add filter, filterStrict utils --- README.md | 36 +++++++++++++++++++ src/filter.js | 15 ++++++++ src/filter.spec.js | 51 +++++++++++++++++++++++++++ src/filter_strict.js | 15 ++++++++ src/filter_strict.spec.js | 74 +++++++++++++++++++++++++++++++++++++++ src/index.js | 4 +++ src/index.spec.js | 10 ++++++ 7 files changed, 205 insertions(+) create mode 100644 src/filter.js create mode 100644 src/filter.spec.js create mode 100644 src/filter_strict.js create mode 100644 src/filter_strict.spec.js diff --git a/README.md b/README.md index e476c9d..1e934f3 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ A collection of array-related async utilities. ### Table of contents * [`asyncEvery()`](#asyncEvery) +* [`asyncFilter()`](#asyncFilter) +* [`asyncFilterStrict()`](#asyncFilterStrict) * [`asyncForEach()`](#asyncForEach) * [`asyncForEachStrict()`](#asyncForEachStrict) * [`asyncMap()`](#asyncMap) @@ -36,6 +38,40 @@ import { asyncEvery } from '@wojtekmaj/async-array-utils'; const largerThanZero = await asyncEvery([1, 2, 3], async (el) => el > 0); // true ``` +### `asyncFilter()` + +Creates a new array with all elements that pass the test implemented by the provided asynchronous function. + +Note: For optimization purposes, all iterations are ran concurrently. If you rely on any side effects, consider `asyncFilterStrict()` instead. + +#### Sample usage + +```js +import { asyncFilter } from '@wojtekmaj/async-array-utils'; + +const asyncFilteredArr = await asyncFilter([1, 2, 3], async (el) => el > 1); // [2, 3] +``` + +### `asyncFilterStrict()` + +Like `asyncFilter()`, but runs iterations non-concurrently. + +#### Sample usage + +```js +import { asyncFilterStrict } from '@wojtekmaj/async-array-utils'; + +const indexes = []; +await asyncFilterStrict( + [1, 2, 3], + async (el, index) => { + indexes.push(index); + return el > 1; + }, +); // [2, 3] +console.log(indexes); // [0, 1, 2] +``` + ### `asyncForEach()` Executes a provided asynchronous function once for each array element. diff --git a/src/filter.js b/src/filter.js new file mode 100644 index 0000000..3887351 --- /dev/null +++ b/src/filter.js @@ -0,0 +1,15 @@ +import asyncForEach from './forEach'; + +export default function asyncSome(arr, fn) { + const result = []; + + // eslint-disable-next-line no-shadow + return asyncForEach(arr, async (cur, idx, arr) => { + const cond = await fn(cur, idx, arr); + + if (cond) { + result[idx] = cur; + } + }) + .then(() => result.filter((cur, idx) => idx in result)); +} diff --git a/src/filter.spec.js b/src/filter.spec.js new file mode 100644 index 0000000..2f412e8 --- /dev/null +++ b/src/filter.spec.js @@ -0,0 +1,51 @@ +import asyncFilter from './filter'; + +import { + inputArr, + largerThanTwo, + largerThanTwoInRandomTime, +} from '../test-utils'; + +describe('asyncFilter()', () => { + it.skip('assertions below are valid for synchronous .filter()', () => { + const filter = jest.fn().mockImplementation(largerThanTwo); + + inputArr.filter(filter); + + expect.assertions(1 + inputArr.length); + + expect(filter).toHaveBeenCalledTimes(inputArr.length); + inputArr.forEach((el, idx) => { + expect(filter).toHaveBeenCalledWith(el, idx, inputArr); + }); + }); + + it('iterates over values properly', async () => { + const filter = jest.fn().mockImplementation(largerThanTwoInRandomTime); + + await asyncFilter(inputArr, filter); + + expect.assertions(1 + inputArr.length); + + expect(filter).toHaveBeenCalledTimes(inputArr.length); + inputArr.forEach((el, idx) => { + expect(filter).toHaveBeenCalledWith(el, idx, inputArr); + }); + }); + + it.skip('assertions below are valid for synchronous .filter()', () => { + const filter = jest.fn().mockImplementation(largerThanTwo); + + const result = inputArr.filter(filter); + + expect(result).toEqual([3, 4, 5, 6, 7, 8, 9, 10]); + }); + + it('filters array properly', async () => { + const filter = jest.fn().mockImplementation(largerThanTwoInRandomTime); + + const result = await asyncFilter(inputArr, filter); + + expect(result).toEqual([3, 4, 5, 6, 7, 8, 9, 10]); + }); +}); diff --git a/src/filter_strict.js b/src/filter_strict.js new file mode 100644 index 0000000..ee4e639 --- /dev/null +++ b/src/filter_strict.js @@ -0,0 +1,15 @@ +import asyncForEachStrict from './forEach_strict'; + +export default function asyncFilterStrict(arr, fn) { + const result = []; + + // eslint-disable-next-line no-shadow + return asyncForEachStrict(arr, async (cur, idx, arr) => { + const cond = await fn(cur, idx, arr); + + if (cond) { + result[idx] = cur; + } + }) + .then(() => result.filter((cur, idx) => idx in result)); +} diff --git a/src/filter_strict.spec.js b/src/filter_strict.spec.js new file mode 100644 index 0000000..750b21a --- /dev/null +++ b/src/filter_strict.spec.js @@ -0,0 +1,74 @@ +import asyncFilterStrict from './filter_strict'; + +import { + doubleInputArr, + inputArr, + largerThanTwo, + largerThanTwoInRandomTime, + makePushDuplicate, + makePushDuplicateInRandomTime, +} from '../test-utils'; + +describe('asyncFilterStrict()', () => { + it.skip('assertions below are valid for synchronous .filter()', () => { + const filter = jest.fn().mockImplementation(largerThanTwo); + + inputArr.filter(filter); + + expect.assertions(1 + inputArr.length); + + expect(filter).toHaveBeenCalledTimes(inputArr.length); + inputArr.forEach((el, idx) => { + expect(filter).toHaveBeenCalledWith(el, idx, inputArr); + }); + }); + + it('iterates over values properly', async () => { + const filter = jest.fn().mockImplementation(largerThanTwoInRandomTime); + + await asyncFilterStrict(inputArr, filter); + + expect.assertions(1 + inputArr.length); + + expect(filter).toHaveBeenCalledTimes(inputArr.length); + inputArr.forEach((el, idx) => { + expect(filter).toHaveBeenCalledWith(el, idx, inputArr); + }); + }); + + it.skip('assertions below are valid for synchronous .map()', () => { + const [arr, pushDuplicate] = makePushDuplicate(); + const mapper = jest.fn().mockImplementation(pushDuplicate); + + inputArr.filter(mapper); + + expect(mapper).toHaveBeenCalledTimes(inputArr.length); + expect(arr).toEqual(doubleInputArr); + }); + + it('iterates through an array properly with side effects', async () => { + const [arr, pushDuplicate] = makePushDuplicateInRandomTime(); + const mapper = jest.fn().mockImplementation(pushDuplicate); + + await asyncFilterStrict(inputArr, mapper); + + expect(mapper).toHaveBeenCalledTimes(inputArr.length); + expect(arr).toEqual(doubleInputArr); + }); + + it.skip('assertions below are valid for synchronous .filter()', () => { + const filter = jest.fn().mockImplementation(largerThanTwo); + + const result = inputArr.filter(filter); + + expect(result).toEqual([3, 4, 5, 6, 7, 8, 9, 10]); + }); + + it('filters array properly', async () => { + const filter = jest.fn().mockImplementation(largerThanTwoInRandomTime); + + const result = await asyncFilterStrict(inputArr, filter); + + expect(result).toEqual([3, 4, 5, 6, 7, 8, 9, 10]); + }); +}); diff --git a/src/index.js b/src/index.js index 9b98c2c..f564fbe 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,5 @@ +import asyncFilter from './filter'; +import asyncFilterStrict from './filter_strict'; import asyncForEach from './forEach'; import asyncForEachStrict from './forEach_strict'; import asyncEvery from './every'; @@ -8,6 +10,8 @@ import asyncSome from './some'; import asyncSomeStrict from './some_strict'; export { + asyncFilter, + asyncFilterStrict, asyncForEach, asyncForEachStrict, asyncEvery, diff --git a/src/index.spec.js b/src/index.spec.js index 8971d2c..6adb70a 100644 --- a/src/index.spec.js +++ b/src/index.spec.js @@ -1,4 +1,6 @@ import { + asyncFilter, + asyncFilterStrict, asyncForEach, asyncForEachStrict, asyncEvery, @@ -10,6 +12,14 @@ import { } from './index'; describe('index', () => { + it('has asyncFilter exported properly', () => { + expect(asyncFilter).toBeInstanceOf(Function); + }); + + it('has asyncFilterStrict exported properly', () => { + expect(asyncFilterStrict).toBeInstanceOf(Function); + }); + it('has asyncForEach exported properly', () => { expect(asyncForEach).toBeInstanceOf(Function); }); -- 2.51.2