From c5b568299c6ed4c16ffbda8e2417d7bd192fd53f Mon Sep 17 00:00:00 2001 From: mb Date: Sun, 28 Apr 2024 19:31:59 +0200 Subject: [PATCH] add reshape --- docs/src/config-menu.ts | 8 +++ docs/src/pages/en/array/operator/cartesian.md | 52 ++++++++++++++++ docs/src/pages/en/array/operator/reshape.md | 50 ++++++++++++++++ spec/core/array.spec.ts | 48 +++++++-------- spec/core/array/operator/cartesian.spec.ts | 16 +++++ spec/core/array/operator/reshape.spec.ts | 60 +++++++++++++++++++ src/array/operator/cartesian.ts | 12 ++-- src/array/operator/index.ts | 1 + src/array/operator/reshape.ts | 31 ++++++++++ 9 files changed, 242 insertions(+), 36 deletions(-) create mode 100644 docs/src/pages/en/array/operator/cartesian.md create mode 100644 docs/src/pages/en/array/operator/reshape.md create mode 100644 spec/core/array/operator/reshape.spec.ts create mode 100644 src/array/operator/reshape.ts diff --git a/docs/src/config-menu.ts b/docs/src/config-menu.ts index 060268a..200b21e 100644 --- a/docs/src/config-menu.ts +++ b/docs/src/config-menu.ts @@ -26,6 +26,10 @@ export default { "text": "allPairs", "link": "en/array/operator/allPairs" }, + { + "text": "cartesian", + "link": "en/array/operator/cartesian" + }, { "text": "choose", "link": "en/array/operator/choose" @@ -134,6 +138,10 @@ export default { "text": "reduce", "link": "en/array/operator/reduce" }, + { + "text": "reshape", + "link": "en/array/operator/reshape" + }, { "text": "skip", "link": "en/array/operator/skip" diff --git a/docs/src/pages/en/array/operator/cartesian.md b/docs/src/pages/en/array/operator/cartesian.md new file mode 100644 index 0000000..22a5abc --- /dev/null +++ b/docs/src/pages/en/array/operator/cartesian.md @@ -0,0 +1,52 @@ +--- +title: cartesian +description: cartesian +layout: ../../../../layouts/MainLayout.astro +--- + +## Description +The `cartesian` function calculates the Cartesian product of multiple arrays. It takes a variable number of arrays as arguments and returns an array of arrays representing all possible combinations of elements from the input arrays. + +## Parameters +- `...arrays`: Arrays of elements. + +## Returns +An array of arrays representing the Cartesian product of the input arrays. + +## Type + +```ts +type cartesian = (dimensions: [number,]) => (a: A[]) => [A][]; +type cartesian = (dimensions: [number, number,]) => (a: A[]) => [A][][]; +//... +type cartesian = (dimensions: [number, number, number, number, number, number, number, number]) => (a: A[]) => [A][][][][][][][][]; +``` + + +## Example +```typescript +import { cartesian } from './path/to/cartesian'; + +const result = cartesian([1, 2], ['a', 'b', 'c'], [true, false]); + +console.log(result); +// Output: [ +// [ +// [[1,"a",true],[1,"a",false]], +// [[1,"b",true],[1,"b",false]], +// [[1,"c",true],[1,"c",false]] +// ], +// [ +// [[2,"a",true],[2,"a",false]], +// [[2,"b",true],[2,"b",false]], +// [[2,"c",true],[2,"c",false]] +// ] +// ] +``` + + + +## See Also + +- [allCombinations](./allCombinations) +- [reshape](./reshape) diff --git a/docs/src/pages/en/array/operator/reshape.md b/docs/src/pages/en/array/operator/reshape.md new file mode 100644 index 0000000..ce7222a --- /dev/null +++ b/docs/src/pages/en/array/operator/reshape.md @@ -0,0 +1,50 @@ +--- +title: reshape +description: reshape +layout: ../../../../layouts/MainLayout.astro +--- + +## Description +The `reshape` function is designed to reshape a flat array into a multi-dimensional array according to the specified dimensions. It takes an array of dimensions and returns a function that accepts a flat array as input and returns a multi-dimensional array based on the provided dimensions. + +## Parameters +- `dimensions`: An array of numbers representing the desired dimensions for the output array. + +## Returns +A function that takes an array as input and returns a multi-dimensional array based on the specified dimensions. + +## Throws +- `Error`: If the total size of the dimensions does not match the length of the input array. + +## Type + +```ts +type reshape = (dimensions: [number,]) => (a: A[]) => [A][]; +type reshape = (dimensions: [number, number,]) => (a: A[]) => [A][][]; +//... +type reshape = (dimensions: [number, number, number, number, number, number, number, number]) => (a: A[]) => [A][][][][][][][][]; +``` + +## Example + +```typescript +import { reshape } from 'fnxt/array'; + +const dimensions = [2, 3, 2]; +const fn = reshape(dimensions); + +const flatArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; +const multiDimensionalArray = fn(flatArray); + +console.log(multiDimensionalArray); +// Output: [ +// [[1, 2], [3, 4], [5, 6]], +// [[7, 8], [9, 10], [11, 12]] +// ] +``` + + +## See Also + +- [allCombinations](./allCombinations) +- [cartesian](./cartesian) \ No newline at end of file diff --git a/spec/core/array.spec.ts b/spec/core/array.spec.ts index b6da8e8..a34e3e1 100644 --- a/spec/core/array.spec.ts +++ b/spec/core/array.spec.ts @@ -12,34 +12,26 @@ describe('array', () => { describe('operator', () => { const operators = [ - 'allCombinations', 'allPairs', 'append', - 'average', 'averageBy', 'cartesian','choose', - 'chunkBySize', 'collect', 'compareWith', - 'concat', 'contains', 'copy', - 'countBy', 'distinctBy', 'every', - 'exists', 'fill', 'filter', - 'find', 'findBack', 'findIndex', - 'findIndexBack', 'flatten', 'fold', - 'foldBack', 'forall', 'groupBy', 'head', - 'init', 'insertAt', 'interleave', 'isEmpty', - 'iter', 'last', 'length', - 'map', 'maxBy', 'minBy', - 'pairwise','pairwiseWith', 'partition', 'push', - 'reduce', 'reduceBack', 'replicate', - 'rev', 'rotate', 'rotateBack', - 'scan', 'scanBack', 'skip', - 'skipWhile', 'sort', 'sortBy', - 'sortByDescending', 'sortDescending', 'sortInPlace', - 'sortInPlace', 'sortInPlaceBy', 'sortInPlaceWith', - 'sortInPlaceWith', 'sortWith', 'sortWith', - 'splitAt', 'splitInto', 'stride', 'strideWindowed', - 'sum', 'sumBy', 'remove', 'tail', 'take', - 'takeWhile', 'takeWhileInclusive', 'transpose', - 'truncate', 'tryFind', 'tryFindBack', - 'tryFindIndex', 'tryFindIndexBack', 'tryHead', - 'tryLast', 'uniqueBy', 'updateAt', - 'where', 'windowed', 'zip', - 'zip3', 'zipWith' + /* A */ 'allCombinations', 'allPairs', 'append', 'average', 'averageBy', + /* C */ 'cartesian', 'choose', 'chunkBySize', 'collect', 'compareWith', 'concat', 'contains', 'copy', 'countBy', + /* D */ 'distinctBy', + /* E */ 'every', 'exists', + /* F */ 'fill', 'filter', 'find', 'findBack', 'findIndex', 'findIndexBack', 'flatten', 'fold', 'foldBack', 'forall', + /* G */ 'groupBy', + /* H */ 'head', + /* I */ 'init', 'insertAt', 'interleave', 'isEmpty', 'iter', + /* L */ 'last', 'length', + /* M */ 'map', 'maxBy', 'minBy', + /* P */ 'pairwise', 'pairwiseWith', 'partition', 'push', + /* R */ 'reduce', 'reduceBack', 'remove', 'replicate', 'reshape', 'rev', 'rotate', 'rotateBack', + /* S */ 'scan', 'scanBack', 'skip', 'skipWhile', 'sort', 'sortBy', 'sortByDescending', 'sortDescending', + 'sortInPlace', 'sortInPlace', 'sortInPlaceBy', 'sortInPlaceWith', 'sortInPlaceWith', 'sortWith', + 'sortWith', 'splitAt', 'splitInto', 'stride', 'strideWindowed', 'sum', 'sumBy', + /* T */ 'tail', 'take', 'takeWhile', 'takeWhileInclusive', 'transpose', 'truncate', 'tryFind', 'tryFindBack', + 'tryFindIndex', 'tryFindIndexBack', 'tryHead', 'tryLast', + /* U */ 'uniqueBy', 'updateAt', + /* W */ 'where', 'windowed', + /* Z */ 'zip', 'zip3', 'zipWith' ] .sort(); operators.map(name => './array/operator/' + name + '.spec') diff --git a/spec/core/array/operator/cartesian.spec.ts b/spec/core/array/operator/cartesian.spec.ts index ef341a4..05f0b3e 100644 --- a/spec/core/array/operator/cartesian.spec.ts +++ b/spec/core/array/operator/cartesian.spec.ts @@ -36,6 +36,22 @@ describe('cartesian', () => { ]); }); + it('should cartesian [1, 2], ["a", "b", "c"], [true, false]', () => { +console.log(JSON.stringify(cartesian([1, 2], ['a', 'b', 'c'], [true, false]))); + expect(cartesian([1, 2], ['a', 'b', 'c'], [true, false])).to.eql([ + [ + [[1,"a",true],[1,"a",false]], + [[1,"b",true],[1,"b",false]], + [[1,"c",true],[1,"c",false]] + ], + [ + [[2,"a",true],[2,"a",false]], + [[2,"b",true],[2,"b",false]], + [[2,"c",true],[2,"c",false]] + ] + ]); + }); + it('should cartesian [3,4,2]', () => { const fn = cartesian; diff --git a/spec/core/array/operator/reshape.spec.ts b/spec/core/array/operator/reshape.spec.ts new file mode 100644 index 0000000..914e6d1 --- /dev/null +++ b/spec/core/array/operator/reshape.spec.ts @@ -0,0 +1,60 @@ +import {expect} from 'chai'; +import {reshape} from '../../../../src/array/'; + +describe('reshape', () => { + + + it('should reshape []', () => { + + const fn = reshape([]); + expect(fn([])).to.eql([]); + }); + + it('should reshape [1]', () => { + + const fn = reshape([1]); + expect(fn([1])).to.eql([1]); + }); + + it('should reshape [2]', () => { + + const fn = reshape([1, 2]); + expect(fn([1, 2])).to.eql([ + [1, 2], + ]); + }); + + it('should reshape [1,2]', () => { + + const fn = reshape([1, 2]); + expect(fn([1, 2])).to.eql([ + [1, 2], + ]); + }); + it('should reshape [2,3,2]', () => { + + const fn = reshape([2, 3, 2]); + expect(fn([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])).to.eql( + [ + [[1, 2], [3, 4], [5, 6]], + [[7, 8], [9, 10], [11, 12]] + ] + ); + }); + it('should reshape [4, 3, 1]', () => { + + const fn = reshape([4, 3, 1]); + expect(fn([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])).to.eql( + [[[1], [2], [3]], [[4], [5], [6]], [[7], [8], [9]], [[10], [11], [12]]] + ); + }); + it('should reshape [4, 3, 1]', () => { + + const fn = reshape([4, 3, 1]); + expect(() => fn([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])).to.throws(); + }); + + +}); + + diff --git a/src/array/operator/cartesian.ts b/src/array/operator/cartesian.ts index 44c70bd..54ce5fa 100644 --- a/src/array/operator/cartesian.ts +++ b/src/array/operator/cartesian.ts @@ -1,5 +1,5 @@ -import {chunkBySize} from './chunkBySize'; import {allCombinations} from './allCombinations'; +import {reshape} from './reshape'; export function cartesian(): []; export function cartesian(a: A[]): [A][]; @@ -15,13 +15,9 @@ export function cartesian(a: A[], b: B[], c: C[], d: D[] export function cartesian(...arrays: unknown[][]): unknown[][] { // @ts-ignore - let res: any[] = allCombinations(...arrays); - - for (let i = arrays.length - 1; i > 0; i--) { - const size = arrays[i].length; - res = chunkBySize(size)(res); - } - return res; + const res: any[] = allCombinations(...arrays); + const dimensions = arrays.map(a => a.length) + return reshape(dimensions)(res) as any[] } diff --git a/src/array/operator/index.ts b/src/array/operator/index.ts index 5aa030a..577640f 100644 --- a/src/array/operator/index.ts +++ b/src/array/operator/index.ts @@ -45,6 +45,7 @@ export * from './reduce'; export * from './reduceBack'; export * from './remove'; export * from './replicate'; +export * from './reshape'; export * from './rev'; export * from './rotate'; export * from './rotateBack'; diff --git a/src/array/operator/reshape.ts b/src/array/operator/reshape.ts new file mode 100644 index 0000000..98cb4e4 --- /dev/null +++ b/src/array/operator/reshape.ts @@ -0,0 +1,31 @@ +import {chunkBySize} from './chunkBySize'; + +export function reshape(dimensions: [number,]): (a: A[]) => [A][]; +export function reshape(dimensions: [number, number,]): (a: A[]) => [A][][]; +export function reshape(dimensions: [number, number, number,]): (a: A[]) => [A][][][]; +export function reshape(dimensions: [number, number, number, number,]): (a: A[]) => [A][][][][]; +export function reshape(dimensions: [number, number, number, number, number,]): (a: A[]) => [A][][][][][]; +export function reshape(dimensions: [number, number, number, number, number, number,]): (a: A[]) => [A][][][][][][]; +export function reshape(dimensions: [number, number, number, number, number, number, number,]): (a: A[]) => [A][][][][][][][]; +export function reshape(dimensions: [number, number, number, number, number, number, number, number]): (a: A[]) => [A][][][][][][][][]; +export function reshape(dimensions: number[]): (a: A[]) => any[]; + +export function reshape(dimensions: number[]) { + const size = dimensions.length ? dimensions.reduce((a, b) => a * b, 1) : 0; + return (arrays: unknown[]): unknown[] => { + if (size !== arrays.length) { + throw new Error(dimensions.join(' * ') + ' must equal array length'); + } + if (size === 0) { + return []; + } + // @ts-ignore + let res: any[] = arrays; + for (let i = dimensions.length - 1; i >= 0; i--) { + res = chunkBySize(dimensions[i])(res); + } + return res[0]; + + }; +} +