diff --git a/README.md b/README.md index 16c347e..020ecb6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # n-tuple-array -Get a **`configurable`** amount of items when iterating over a JavaScript array, instead of a single item that arrays provide per iteration, by default. +Get a **`configurable`** amount of items when iterating over a JavaScript array, instead of a single item that arrays provide per iteration, by default. + +Put differently, easily and safely retrieve a configurable number of items from an array, without having to manipulate array bounds or indices. ## Motivation @@ -8,7 +10,7 @@ Get a **`configurable`** amount of items when iterating over a JavaScript array, Imagine that you received a large collection of coordinates (latitude and longitude), but they were sent as a flat array of values to speed up the data transfer. -`n-tuple-array` can help you get out the coordinates in pairs (i.e their logical representation), such that you'd go +`n-tuple-array` can help you get out the coordinates in pairs (i.e their logical representation), such that you'd __easily__ go **from** ```json @@ -16,17 +18,24 @@ as a flat array of values to speed up the data transfer. ["5.7225", "-9.6273", "2.68452", "-30.9501", ...] ``` -**to** +**to** +```json +// coordinates in pairs +[["5.7225", "-9.6273"], ["2.68452", "-30.9501"], ...] +``` + +**using** ```javascript // the iterable will generate pairs by default const coordsIterable = tuplesFromArray({ list: flatCoords }); -// using for..of, get pairs as ["5.7225", "-9.6273"] ... +// with for..of, get pairs as ["5.7225", "-9.6273"] ... for (const pair of coordsIterable) { console.log(pair); } -// OR manipulate pairs with regular array +// OR +// manipulate pairs with regular array // functions like map, filter, forEach ... const coordsInPairs = Array.from(coordsIterable); console.log(Array.isArray(coordsInPairs)); // true @@ -41,29 +50,6 @@ coordsInPairs }); ``` -### Some Real World Examples - -#### 1. Wole Joko - -I first tried my hands on this concept when [fleshing out wole-joko](https://github.com/chalu/wole-joko/blob/dev/src/js/utils.js#L57-L92), which strated as a _live coding task_ I was asked to do in an engineering manager interview :man_shrugging -It was a simulation of people entering an event hall to get seated, but **only two** could get in at a time - https://wole-joko.netlify.app/ - -#### 2. Execute max of `N` async tasks - -![](./assets/the-dax-js-challenge.png "JS challenge by @thdxr") - -> The below was adapted for more concise terminal output - -`n-tuple-array` solution. View [code here](https://github.com/chalu/n-tuple-array/blob/main/src/examples/classic.ts#L6-L40)
-![](./assets/demo-classic.png "n-tuple-array solution") -

- -`n-tuple-array` solution demo
-`n-tuple-array` solution demo
-![](./assets/ntuple-array-demo-optimized.gif "n-tuple-array solution demo") - - - ## Setup & Usage ```bash @@ -72,7 +58,6 @@ npm install @chalu/n-tuple-array ```javascript const { tuplesFromArray } = require('@chalu/n-tuple-array'); -const { tuplesFromArray } = require('@chalu/n-tuple-array'); // some setup const numbers = Array.from({length: 100}, (_, i) => i + 1); @@ -86,16 +71,39 @@ const isEven = (item) => { return true; }; -// use the lib +// use the lib, get batches of <= 5 nums +// from the collection in numbers above const quintetIterator = tuplesFromArray({ list: numbers, maxItems: 5, match: isEven }); +// since quintetIterator uses the isEven match +// function, give us only even numbers in fives for (const quintet of quintetIterator) { // prints [ 2, 4, 6, 8, 10 ] ... [ 92, 94, 96, 98, 100 ] console.log(quintet); } ``` +### Some Real World Examples + +#### 1. Wole Joko + +I first tried my hands on this concept when [fleshing out wole-joko](https://github.com/chalu/wole-joko/blob/dev/src/js/utils.js#L57-L92), which strated as a _live coding task_ I was asked to do in an engineering manager interview :man_shrugging +It was a simulation of people entering an event hall to get seated, but **only two** could get in at a time - https://wole-joko.netlify.app/ + +#### 2. Execute max of `N` async tasks at the same time + +![](./assets/the-dax-js-challenge.png "JS challenge by @thdxr") + +> The below was adapted for more concise terminal output + +`n-tuple-array` solution. View [code here](https://github.com/chalu/n-tuple-array/blob/main/src/examples/classic.ts#L6-L40)
+![](./assets/demo-classic.png "n-tuple-array solution") +

+ +`n-tuple-array` solution demo
+![](./assets/ntuple-array-demo-optimized.gif) + See more examples in [src/examples](./src/examples/) diff --git a/assets/the-dax-js-challenge.png b/assets/the-dax-js-challenge.png index 41b9041..0e19c02 100644 Binary files a/assets/the-dax-js-challenge.png and b/assets/the-dax-js-challenge.png differ diff --git a/dist/cjs/types/index.d.ts b/dist/cjs/types/index.d.ts index cb5aa72..cbe5290 100644 --- a/dist/cjs/types/index.d.ts +++ b/dist/cjs/types/index.d.ts @@ -1,13 +1,53 @@ -type Item = T | undefined; -type Value = Array>; export type Matcher = (item: T | unknown) => boolean; export type TupleConfig = { + /** + * The input array to use + */ list: T[]; + /** + * The max number of items to return from the input array, per iteration + * @defaultValue 2 + */ maxItems?: number; + /** + * When provided, a function used to determine which items in the input array + * are eligible to be included, per iteration + */ match?: Matcher; }; +/** + * An exception than can be thrown if there is no input array, `maxItems` is <= 0 or is not + * a number, or `match` is not a function + */ export declare class InvalidInvocationParameterError extends Error { } -export declare const tuplesFromArray: (config: TupleConfig) => Iterable>; +/** + * Returns an iterable iterator that ouputs a configured + * list of items when iterating over a given array + * + * @typeParam T - Type of items the input list contains + * + * @param config - An object to indicate the input array `config.list`, and set the + * max size of items per interation `config.maxItems`. You can also optionally specify `config.match` + * as a function that should return true to filter in items from the input array + * (or false to filter them out) when deciding what items is to be included per iteration + * + * @function + * @throws InvalidInvocationParameterError + * This exception is thrown if there is no input array, `maxItems` is <= 0 or is not + * a number, or `match` is not a function + * + * @returns an IterableIterator + * + * @example + * Here is an example that will get max of 3 items from + * each iteration on the returned iterable + * ```javascript + * const iterable = tuplesFromArray({ + * list:[], maxSize: 3, match: (itm) => !!itm + * }); + * ``` + */ +export declare const tuplesFromArray: (config: TupleConfig) => IterableIterator<(T | undefined)[]>; export default tuplesFromArray; //# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/package.json b/package.json index ab02be3..87dcfb1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@chalu/n-tuple-array", - "version": "0.1.6", - "description": "Get a specified amount of items when iterating over a JavaScript array, instead of just a single item that arrays provide!", + "version": "0.1.7", + "description": "Configure and get a specified amount of items when iterating over a JavaScript array.", "main": "./dist/cjs/index.js", "module": "dist/esm/index.mjs", "types": "./dist/cjs/types/index.d.ts", diff --git a/src/examples/js/concurrent.js b/src/examples/js/concurrent.js index 0ad348e..3eed0d2 100644 --- a/src/examples/js/concurrent.js +++ b/src/examples/js/concurrent.js @@ -14,6 +14,7 @@ const processTask = async (todo, batchId) => { }; const executeConcurrent = async (todos, maxBatchSize) => { + console.log(`\ndo <= ${maxBatchSize} of ${todos.length} tasks (concurrently), at any given time\n`); const done = []; const todosCopy = todos.slice(); const worker = async (_, batchIndex) => { @@ -35,5 +36,5 @@ const executeConcurrent = async (todos, maxBatchSize) => { const maxTasksPerTime = 2; const allDone = await executeConcurrent(tasks, maxTasksPerTime); - console.log(allDone); + console.log('\n', allDone); })(); diff --git a/src/examples/js/with-lib.mjs b/src/examples/js/with-lib.mjs index 02a5c6f..8bb31e6 100644 --- a/src/examples/js/with-lib.mjs +++ b/src/examples/js/with-lib.mjs @@ -16,13 +16,13 @@ const processTask = async (todo, batchId) => { }; const executeWitLibrary = async (todos, maxBatchSize) => { - console.log(`do <= ${maxBatchSize} of ${todos.length} tasks, at any given time`); + console.log(`\ndo <= ${maxBatchSize} of ${todos.length} tasks, at any given time (with library)`); const done = []; let batchIndex = 1; const todosBatchIterable = tuplesFromArray({list: todos, maxItems: maxBatchSize}); for (const batch of todosBatchIterable) { - console.log(`----- starting batch [${batchIndex}], ${batch.length} todos`); + console.log(`\n----- starting batch [${batchIndex}], ${batch.length} todos`); const results = await Promise.allSettled( batch.map(todo => processTask(todo, batchIndex)), ); @@ -39,5 +39,5 @@ const executeWitLibrary = async (todos, maxBatchSize) => { const maxTasksPerTime = 2; const allDone = await executeWitLibrary(tasks, maxTasksPerTime); - console.log(allDone); + console.log('\n', allDone); })(); diff --git a/src/examples/js/without-lib.js b/src/examples/js/without-lib.js index e9803b7..5f7de65 100644 --- a/src/examples/js/without-lib.js +++ b/src/examples/js/without-lib.js @@ -15,7 +15,7 @@ const processTask = async (todo, batchId) => { // Everywhere start/end is used is repititive and error prone const executeWithoutLibrary = async (todos, maxBatchSize) => { - console.log(`do <= ${maxBatchSize} of ${todos.length} tasks, at any given time`); + console.log(`\ndo <= ${maxBatchSize} of ${todos.length} tasks, at any given time (no library)`); const done = []; let start = 0; @@ -23,7 +23,7 @@ const executeWithoutLibrary = async (todos, maxBatchSize) => { let batchIndex = 1; let batch = todos.slice(start, end); while (batch.length > 0) { - console.log(`----- starting batch [${batchIndex}], ${batch.length} todos`); + console.log(`\n----- starting batch [${batchIndex}], ${batch.length} todos`); const results = await Promise.allSettled( batch.map(todo => processTask(todo, batchIndex)), ); @@ -48,5 +48,5 @@ const executeWithoutLibrary = async (todos, maxBatchSize) => { const maxTasksPerTime = 2; const allDone = await executeWithoutLibrary(tasks, maxTasksPerTime); - console.log(allDone); + console.log('\n', allDone); })();