Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
e7db04f
init
chalu Feb 15, 2024
0132918
get started with the implementation
chalu Feb 15, 2024
f6241ef
allow caller filter in elements with a match function
chalu Feb 15, 2024
9ea6c84
setup unit tests
chalu Feb 15, 2024
477c5df
add smoke tests for the list parameter
chalu Feb 15, 2024
a8513ce
add smoke tests for maxItems param
chalu Feb 15, 2024
87d8c36
add smoke tests for the match param
chalu Feb 15, 2024
9b41f0a
refactor param validation into a separate function
chalu Feb 15, 2024
3a9dfbf
add basic CI workflow
chalu Feb 15, 2024
72bf07d
re-add CI workflow file
chalu Feb 15, 2024
3795560
add catch all branch in CI workflow
chalu Feb 15, 2024
e2c42cd
try install pnpm before node in CI workflow
chalu Feb 15, 2024
7c81b61
attempt display test summary on GHA overview page
chalu Feb 15, 2024
18b2461
add TAP reporter for tests in CI
chalu Feb 15, 2024
15eada8
attempt junit reporter for summary in CI
chalu Feb 15, 2024
66f93e3
remove TAP reporter
chalu Feb 15, 2024
e1efbf2
improve setup for tests
chalu Feb 16, 2024
97db447
add matchers from jest-extended
chalu Feb 16, 2024
5aa4f84
add smoke tests
chalu Feb 16, 2024
ac99976
add festures tests
chalu Feb 16, 2024
ae39890
enforce code style with XO
chalu Feb 16, 2024
433c458
add pre-commit hook to run lint, build, and test scripts
chalu Feb 16, 2024
6c9f083
simplify pre-commit script
chalu Feb 16, 2024
c0646b8
add built files in dist folder
chalu Feb 16, 2024
ea42403
Merge pull request #5 from chalu/add-unit-tests
chalu Feb 16, 2024
abb0cd8
add docs and samples (#7)
chalu Feb 17, 2024
5c8e09d
Merge branch 'main' into dev
chalu Feb 17, 2024
6c301d0
Go publish (#9)
chalu Feb 18, 2024
2f62a5d
Merge branch 'main' into dev
chalu Feb 18, 2024
4dc022e
Go publish (#11)
chalu Feb 18, 2024
8d881d8
Merge branch 'main' into dev
chalu Feb 18, 2024
36d8a7c
Dry run publishing on dev (#13)
chalu Feb 18, 2024
2acd5cd
Dry run release (#14)
chalu Feb 18, 2024
d4dc284
Attempt release from GHA (#16)
chalu Feb 18, 2024
a0397ab
Atempt publish (#17)
chalu Feb 18, 2024
3b00a61
Attempt auto publish (#18)
chalu Feb 18, 2024
875ad62
Go public (#19)
chalu Feb 18, 2024
9f9b4c7
Merge branch 'main' into dev
chalu Feb 18, 2024
b79a13b
patch current release (#21)
chalu Mar 6, 2024
650925c
resolve conflicts with main
chalu Mar 6, 2024
5e0e592
Patch release (#23)
chalu Mar 11, 2024
ba54c43
Merge branch 'main' into dev
chalu Mar 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 38 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,41 @@
# 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

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
// flat coordinates
["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
Expand All @@ -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) <br>
![](./assets/demo-classic.png "n-tuple-array solution")
<br> <br>

`n-tuple-array` solution demo <br>
`n-tuple-array` solution demo <br>
![](./assets/ntuple-array-demo-optimized.gif "n-tuple-array solution demo")



## Setup & Usage

```bash
Expand All @@ -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);
Expand All @@ -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) <br>
![](./assets/demo-classic.png "n-tuple-array solution")
<br> <br>

`n-tuple-array` solution demo <br>
![](./assets/ntuple-array-demo-optimized.gif)

See more examples in [src/examples](./src/examples/)

Binary file modified assets/the-dax-js-challenge.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 changes: 43 additions & 3 deletions dist/cjs/types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,53 @@
type Item<T> = T | undefined;
type Value<T> = Array<Item<T>>;
export type Matcher<T> = (item: T | unknown) => boolean;
export type TupleConfig<T> = {
/**
* 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<T>;
};
/**
* 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: <T>(config: TupleConfig<T>) => Iterable<Value<T>>;
/**
* 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: <T>(config: TupleConfig<T>) => IterableIterator<(T | undefined)[]>;
export default tuplesFromArray;
//# sourceMappingURL=index.d.ts.map
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
3 changes: 2 additions & 1 deletion src/examples/js/concurrent.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand All @@ -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);
})();
6 changes: 3 additions & 3 deletions src/examples/js/with-lib.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
);
Expand All @@ -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);
})();
6 changes: 3 additions & 3 deletions src/examples/js/without-lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ 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;
let end = start + 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)),
);
Expand All @@ -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);
})();