Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
e3d7111
ready to publish
chalu Feb 18, 2024
51add33
add CD workflow
chalu Feb 18, 2024
16d3398
lint only changed TS or JS files
chalu Feb 18, 2024
d0de98d
fix CD job name
chalu Feb 18, 2024
83348c4
run CI only on PR to dev or main
chalu Feb 18, 2024
d8979e9
dry run package publishing
chalu Feb 18, 2024
c29a94d
merge branch 'main' into go-publish
chalu Feb 18, 2024
829d598
Merge branch 'dev' into go-publish
chalu Feb 18, 2024
2b39e3e
dry run publish on dev branch
chalu Feb 18, 2024
55a6922
sync local and remote branches
chalu Feb 18, 2024
0cfbc03
get production ready
chalu Feb 18, 2024
bbf5f0b
make a default tsconfig from the ESM config
chalu Feb 18, 2024
41ba737
dry run release from GHA
chalu Feb 18, 2024
01996d9
Merge branch 'dev' into go-publish
chalu Feb 18, 2024
d706086
prune GHA runs
chalu Feb 18, 2024
a3ba02f
update with merge conflict resolution commit
chalu Feb 18, 2024
0d3d8b1
attempt release from GHA
chalu Feb 18, 2024
670b7c1
attempt release from GHA
chalu Feb 18, 2024
d837f27
Merge branch 'dev' into go-publish
chalu Feb 18, 2024
b528f0c
fix package json file
chalu Feb 18, 2024
f0a3f5f
fix version
chalu Feb 18, 2024
8fd0e34
Merge branch 'dev' into go-publish
chalu Feb 18, 2024
2a29104
remove merged condition
chalu Feb 18, 2024
db42545
fix version
chalu Feb 18, 2024
f176ab0
fix version
chalu Feb 18, 2024
1d5adb1
use latest tag
chalu Feb 18, 2024
387b3e2
fix release workflow
chalu Feb 18, 2024
dd323c0
prepare first public release
chalu Feb 18, 2024
33b9183
set first public release
chalu Feb 18, 2024
120dd89
Merge branch 'dev' into go-publish
chalu Feb 18, 2024
dba7f3e
remove files filter from lint script
chalu Feb 28, 2024
95e196e
simplify types used
chalu Feb 28, 2024
6f6a4ad
refine lint script for pre-commit hook
chalu Mar 5, 2024
b41eb14
fix cjs and esm modules
chalu Mar 5, 2024
ba0f900
add updated build
chalu Mar 5, 2024
994f562
fix lint errors in esm example code
chalu Mar 5, 2024
854d604
add more JS examples
chalu Mar 6, 2024
b0f35e3
document what is exported
chalu Mar 6, 2024
d076ec3
document what is exported
chalu Mar 6, 2024
1e8584a
update build
chalu Mar 6, 2024
6f347fb
bump patch version
chalu Mar 6, 2024
ff1ef63
Merge branch 'dev' into patch-release
chalu Mar 6, 2024
99e91ef
cleanup project README
chalu Mar 10, 2024
6128432
update js examples for cleaner output
chalu Mar 10, 2024
655a420
update concurrent example for cleaner output
chalu Mar 10, 2024
6eeeea0
bump version for patch
chalu Mar 11, 2024
86930cc
Merge branch 'dev' into patch-release
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);
})();