From 671f077197fa084b5245bccca0a958909e80e7ce Mon Sep 17 00:00:00 2001 From: mb Date: Mon, 22 Apr 2024 20:44:25 +0200 Subject: [PATCH] add cartesian operation --- spec/core/array.spec.ts | 2 +- .../array/operator/allCombinations.spec.ts | 15 ++- spec/core/array/operator/cartesian.spec.ts | 113 ++++++++++++++++++ src/array/operator/allCombinations.ts | 3 +- src/array/operator/cartesian.ts | 27 +++++ src/array/operator/index.ts | 1 + 6 files changed, 156 insertions(+), 5 deletions(-) create mode 100644 spec/core/array/operator/cartesian.spec.ts create mode 100644 src/array/operator/cartesian.ts diff --git a/spec/core/array.spec.ts b/spec/core/array.spec.ts index 3da1e3c..b6da8e8 100644 --- a/spec/core/array.spec.ts +++ b/spec/core/array.spec.ts @@ -13,7 +13,7 @@ describe('array', () => { describe('operator', () => { const operators = [ 'allCombinations', 'allPairs', 'append', - 'average', 'averageBy', 'choose', + 'average', 'averageBy', 'cartesian','choose', 'chunkBySize', 'collect', 'compareWith', 'concat', 'contains', 'copy', 'countBy', 'distinctBy', 'every', diff --git a/spec/core/array/operator/allCombinations.spec.ts b/spec/core/array/operator/allCombinations.spec.ts index ecc1329..1a6e30d 100644 --- a/spec/core/array/operator/allCombinations.spec.ts +++ b/spec/core/array/operator/allCombinations.spec.ts @@ -5,16 +5,25 @@ import {checkThrow} from '../../../support/checkThrow'; describe('allCombinations', () => { - it('should allCombinations', () => { + it('should allCombinations []', () => { const fn = allCombinations; - expect(fn([1, 2], ['a', 'b'], [4, 5])).to.eql([ + expect(fn()).to.eql([]); + }); + it('should allCombinations [2,2,2]', () => { + + const fn = allCombinations; + expect(fn( + [1, 2], + ['a', 'b'], + [4, 5] + )).to.eql([ [1, 'a', 4], [1, 'a', 5], [1, 'b', 4], [1, 'b', 5], [2, 'a', 4], [2, 'a', 5], [2, 'b', 4], [2, 'b', 5], ]); }); - it('should allCombinations', () => { + it('should allCombinations [4,4,2]', () => { const fn = allCombinations; const expansion = fn(['ace', 'king', 'queen', 'jack'], ['hearts', 'spades', 'diamonds', 'clubs'], ['blue cover', 'red cover']); diff --git a/spec/core/array/operator/cartesian.spec.ts b/spec/core/array/operator/cartesian.spec.ts new file mode 100644 index 0000000..ef341a4 --- /dev/null +++ b/spec/core/array/operator/cartesian.spec.ts @@ -0,0 +1,113 @@ +import {expect} from 'chai'; +import {cartesian} from '../../../../src/array'; +import {checkThrow} from '../../../support/checkThrow'; + +describe('cartesian', () => { + + + it('should cartesian []', () => { + + const fn = cartesian; + expect(fn([])).to.eql([]); + }); + + it('should cartesian [2]', () => { + + const fn = cartesian; + expect(fn([1, 2])).to.eql([ + [1], + [2], + ]); + }); + it('should cartesian [2,3]', () => { + + const fn = cartesian; + expect(fn([1, 2], ['a', 'b', 'c'])).to.eql([ + [ + [1, 'a'], + [1, 'b'], + [1, 'c'], + ], + [ + [2, 'a'], + [2, 'b'], + [2, 'c'], + ], + ]); + }); + + it('should cartesian [3,4,2]', () => { + + const fn = cartesian; + type Cover = 'blue cover' | 'red cover' + type Color = 'hearts' | 'spades' | 'diamonds' | 'clubs' + type Value = 'ace' | 'king' | 'queen' + const covers: Cover[] = ['blue cover', 'red cover']; + const expansion = fn(['ace', 'king', 'queen'] as Value[], ['hearts', 'spades', 'diamonds', 'clubs'] as Color[], covers); + const expected: typeof expansion = [ + [ + [ + ['ace', 'hearts', 'blue cover'], + ['ace', 'hearts', 'red cover'],], + [ + ['ace', 'spades', 'blue cover'], + ['ace', 'spades', 'red cover'], + ], + [ + ['ace', 'diamonds', 'blue cover'], + ['ace', 'diamonds', 'red cover'], + ], + [ + ['ace', 'clubs', 'blue cover'], + ['ace', 'clubs', 'red cover'], + ], + ], + [ + [ + ['king', 'hearts', 'blue cover'], + ['king', 'hearts', 'red cover'], + ], + [ + ['king', 'spades', 'blue cover'], + ['king', 'spades', 'red cover'], + ], + [ + ['king', 'diamonds', 'blue cover'], + ['king', 'diamonds', 'red cover'], + ], + [ + ['king', 'clubs', 'blue cover'], + ['king', 'clubs', 'red cover'], + ], + ], + [ + [ + ['queen', 'hearts', 'blue cover'], + ['queen', 'hearts', 'red cover'], + ], + [ + ['queen', 'spades', 'blue cover'], + ['queen', 'spades', 'red cover'], + ], + [ + ['queen', 'diamonds', 'blue cover'], + ['queen', 'diamonds', 'red cover'], + ], + [ + ['queen', 'clubs', 'blue cover'], + ['queen', 'clubs', 'red cover'], + ], + ] + ]; + expect(expansion).to.eql(expected); + }); + + + it('should throw if null or undefined', () => { + const fn = (e: number[][]) => cartesian(e); + checkThrow(fn); + }); + +}); + + diff --git a/src/array/operator/allCombinations.ts b/src/array/operator/allCombinations.ts index bf6f0e1..4ba10c3 100644 --- a/src/array/operator/allCombinations.ts +++ b/src/array/operator/allCombinations.ts @@ -2,6 +2,7 @@ import {allPairs} from './allPairs'; import {map} from './map'; import {pipe} from 'fnxt/pipe'; +export function allCombinations(): []; export function allCombinations(a: A[]): [A][]; export function allCombinations(a: A[], b: B[]): [A, B][]; export function allCombinations(a: A[], b: B[], c: C[]): [A, B, C][]; @@ -12,7 +13,7 @@ export function allCombinations(a: A[], b: B[], c: C[], d: export function allCombinations(a: A[], b: B[], c: C[], d: D[], e: E[], f: F[], g: G[], h: H[]): [A, B, C, D, E, F, G, H][]; export function allCombinations(...arrays: unknown[][]): unknown[][] { - + if (arrays.length === 0) return []; const fn = ([head, ...tail]: unknown[][]): unknown[][] => tail.length ? pipe(fn, allPairs(head), map(([h, t]) => [h, ...t]))(tail) diff --git a/src/array/operator/cartesian.ts b/src/array/operator/cartesian.ts new file mode 100644 index 0000000..7177acf --- /dev/null +++ b/src/array/operator/cartesian.ts @@ -0,0 +1,27 @@ +import {chunkBySize} from './chunkBySize'; +import {allCombinations} from './allCombinations'; + +export function cartesian(): []; +export function cartesian(a: A[]): [A][]; +export function cartesian(a: A[], b: B[]): [A, B][][]; +export function cartesian(a: A[], b: B[], c: C[]): [A, B, C][][][]; +export function cartesian(a: A[], b: B[], c: C[], d: D[]): [A, B, C, D][][][][]; +export function cartesian(a: A[], b: B[], c: C[], d: D[], e: E[]): [A, B, C, D, E][][][][][]; +export function cartesian(a: A[], b: B[], c: C[], d: D[], e: E[], f: F[]): [A, B, C, D, E, F][][][][][]; +export function cartesian(a: A[], b: B[], c: C[], d: D[], e: E[], f: F[], g: G[]): [A, B, C, D, E, F, G][][][][][][]; +export function cartesian(a: A[], b: B[], c: C[], d: D[], e: E[], f: F[], g: G[], h: H[]): [A, B, C, D, E, F, G, H][][][][][][][]; +export function cartesian(a: A[], b: B[], c: C[], d: D[], e: E[], f: F[], g: G[], h: H[]): [A, B, C, D, E, F, G, H][][][][][][][]; + +export function cartesian(...arrays: unknown[][]): unknown[][] { + + // @ts-ignore + let res: any[] = allCombinations(...arrays); + + for (let i = arrays.length - 1; i > 0; i--) { + let size = arrays[i].length; + res = chunkBySize(size)(res); + } + return res; + +} + diff --git a/src/array/operator/index.ts b/src/array/operator/index.ts index 581f02d..5aa030a 100644 --- a/src/array/operator/index.ts +++ b/src/array/operator/index.ts @@ -3,6 +3,7 @@ export * from './allPairs'; export * from './append'; export * from './average'; export * from './averageBy'; +export * from './cartesian'; export * from './choose'; export * from './chunkBySize'; export * from './collect';