From 1832dec808957e709fe82187f60e68cd2daf0d22 Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Wed, 11 Sep 2024 02:24:03 +0500 Subject: [PATCH 01/11] feat(array): product --- benchmarks/array/product.bench.ts | 30 +++++++++++++++++++ docs/array/product.mdx | 28 +++++++++++++++++ src/array/product.ts | 26 ++++++++++++++++ src/mod.ts | 2 +- tests/array/product.test.ts | 50 +++++++++++++++++++++++++++++++ 5 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 benchmarks/array/product.bench.ts create mode 100644 docs/array/product.mdx create mode 100644 src/array/product.ts create mode 100644 tests/array/product.test.ts diff --git a/benchmarks/array/product.bench.ts b/benchmarks/array/product.bench.ts new file mode 100644 index 00000000..75db3c6f --- /dev/null +++ b/benchmarks/array/product.bench.ts @@ -0,0 +1,30 @@ +import * as _ from 'radashi' + +describe('product', () => { + bench('with no arguments', () => { + _.product([]) + }) + + bench('with single empty array', () => { + _.product([[]]) + }) + + bench('with one non-empty array (n=1)', () => { + _.product([['a', 'b', 'c']]) + }) + + bench('with two small arrays (n=2)', () => { + _.product([ + ['red', 'blue'], + ['fast', 'slow'], + ]) + }) + + bench('with three small arrays (n=3)', () => { + _.product([ + ['red', 'blue'], + ['fast', 'slow'], + ['big', 'small'], + ]) + }) +}) diff --git a/docs/array/product.mdx b/docs/array/product.mdx new file mode 100644 index 00000000..45c9f5cd --- /dev/null +++ b/docs/array/product.mdx @@ -0,0 +1,28 @@ +--- +title: product +description: Perform a Cartesian product of arrays +--- + +### Usage + +Creates an array of all possible combinations (Cartesian product) from the given arrays, where each combination is represented as an array (or tuple) of elements. + +```ts +import * as _ from 'radashi' + +const colors = ['red', 'blue'] +const sizes = ['big', 'small'] +const compliments = ['nice', 'great'] + +_.product([colors, sizes, numbers]) +// => [ +// ['red', 'big', 'nice'], +// ['red', 'big', 'great'], +// ['red', 'small', 'nice'], +// ['red', 'small', 'great'], +// ['blue', 'big', 'nice'], +// ['blue', 'big', 'great'], +// ['blue', 'small', 'nice'], +// ['blue', 'small', 'great'] +// ] +``` diff --git a/src/array/product.ts b/src/array/product.ts new file mode 100644 index 00000000..e2143b32 --- /dev/null +++ b/src/array/product.ts @@ -0,0 +1,26 @@ +/** + * Perform a Cartesian product of arrays, combining all elements from the + * input arrays into all possible combinations. + * + * @see https://radashi.js.org/reference/array/product + * @example + * ```ts + * product([['red', 'blue'], ['big', 'small'], ['fast', 'slow']]) + * // => [['red', 'big', 'fast'], ['red', 'big', 'slow'], ['red', 'small', 'fast'], ['red', 'small', 'slow'], ['blue', 'big', 'fast'], ['blue', 'big', 'slow'], ['blue', 'small', 'fast'], ['blue', 'small', 'slow']] + * ``` + */ +export function product(arrays: T[][]): T[][] { + let out: T[][] = [[]] + for (const array of arrays) { + const result: T[][] = [] + for (const currentArray of out) { + for (const item of array) { + const currentArrayCopy = currentArray.slice() + currentArrayCopy.push(item) + result.push(currentArrayCopy) + } + } + out = result + } + return out +} diff --git a/src/mod.ts b/src/mod.ts index ae241665..c760143b 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -16,6 +16,7 @@ export * from './array/list.ts' export * from './array/mapify.ts' export * from './array/merge.ts' export * from './array/objectify.ts' +export * from './array/product.ts' export * from './array/replace.ts' export * from './array/replaceOrAppend.ts' export * from './array/select.ts' @@ -28,7 +29,6 @@ export * from './array/unique.ts' export * from './array/unzip.ts' export * from './array/zip.ts' export * from './array/zipToObject.ts' - export * from './async/AggregateError.ts' export * from './async/all.ts' export * from './async/defer.ts' diff --git a/tests/array/product.test.ts b/tests/array/product.test.ts new file mode 100644 index 00000000..a29fbc1f --- /dev/null +++ b/tests/array/product.test.ts @@ -0,0 +1,50 @@ +import * as _ from 'radashi' + +describe('product', () => { + test('returns an array containing an empty array when given an empty input array (n=0)', () => { + expect(_.product([])).toEqual([[]]) + }) + test('returns an empty array when given an array containing an empty array (n=1)', () => { + expect(_.product([[]])).toEqual([]) + }) + test('returns an empty array when given multiple empty arrays (n>1)', () => { + expect(_.product([[], [], []])).toEqual([]) + }) + test('returns an empty array when one of the arrays in the input is empty (n>1)', () => { + expect(_.product([['1', '2', '3'], []])).toEqual([]) + }) + test('returns an array of singletons when given a single array (n=1)', () => { + expect(_.product([['1', '2', '3']])).toEqual([['1'], ['2'], ['3']]) + }) + test('performs a correct Cartesian product for two arrays (n=2)', () => { + expect( + _.product([ + ['red', 'blue'], + ['fast', 'slow'], + ]), + ).toEqual([ + ['red', 'fast'], + ['red', 'slow'], + ['blue', 'fast'], + ['blue', 'slow'], + ]) + }) + test('performs a correct Cartesian product for more than two arrays (n>2)', () => { + expect( + _.product([ + ['red', 'blue'], + ['fast', 'slow'], + ['big', 'small'], + ]), + ).toEqual([ + ['red', 'fast', 'big'], + ['red', 'fast', 'small'], + ['red', 'slow', 'big'], + ['red', 'slow', 'small'], + ['blue', 'fast', 'big'], + ['blue', 'fast', 'small'], + ['blue', 'slow', 'big'], + ['blue', 'slow', 'small'], + ]) + }) +}) From 10c90b16248a6641c5884a15b801ae310d3b895f Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Wed, 11 Sep 2024 02:32:23 +0500 Subject: [PATCH 02/11] chore: fix typo --- docs/array/product.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/array/product.mdx b/docs/array/product.mdx index 45c9f5cd..fb3408c8 100644 --- a/docs/array/product.mdx +++ b/docs/array/product.mdx @@ -14,7 +14,7 @@ const colors = ['red', 'blue'] const sizes = ['big', 'small'] const compliments = ['nice', 'great'] -_.product([colors, sizes, numbers]) +_.product([colors, sizes, compliments]) // => [ // ['red', 'big', 'nice'], // ['red', 'big', 'great'], From 7e883ee0528642cac8cd026892b3b1edeefe93db Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Wed, 11 Sep 2024 22:43:39 +0500 Subject: [PATCH 03/11] docs: apply code review suggestions --- docs/array/product.mdx | 5 ++++- src/array/product.ts | 14 +++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/docs/array/product.mdx b/docs/array/product.mdx index fb3408c8..77ea5c60 100644 --- a/docs/array/product.mdx +++ b/docs/array/product.mdx @@ -5,7 +5,10 @@ description: Perform a Cartesian product of arrays ### Usage -Creates an array of all possible combinations (Cartesian product) from the given arrays, where each combination is represented as an array (or tuple) of elements. +Creates an [n-ary Cartesian product](https://en.wikipedia.org/wiki/Cartesian_product#n-ary_Cartesian_product) from the given arrays. +The input is an array of arrays, and the output is an array of arrays representing all +possible combinations where the first element is from the first array, the second element +is from the second array, and so on. ```ts import * as _ from 'radashi' diff --git a/src/array/product.ts b/src/array/product.ts index e2143b32..cbf7f3c1 100644 --- a/src/array/product.ts +++ b/src/array/product.ts @@ -1,12 +1,20 @@ /** - * Perform a Cartesian product of arrays, combining all elements from the - * input arrays into all possible combinations. + * Creates an [n-ary Cartesian product](https://en.wikipedia.org/wiki/Cartesian_product#n-ary_Cartesian_product) from the given arrays. * * @see https://radashi.js.org/reference/array/product * @example * ```ts * product([['red', 'blue'], ['big', 'small'], ['fast', 'slow']]) - * // => [['red', 'big', 'fast'], ['red', 'big', 'slow'], ['red', 'small', 'fast'], ['red', 'small', 'slow'], ['blue', 'big', 'fast'], ['blue', 'big', 'slow'], ['blue', 'small', 'fast'], ['blue', 'small', 'slow']] + * // [ + * // ['red', 'big', 'fast'], + * // ['red', 'big', 'slow'], + * // ['red', 'small', 'fast'], + * // ['red', 'small', 'slow'], + * // ['blue', 'big', 'fast'], + * // ['blue', 'big', 'slow'], + * // ['blue', 'small', 'fast'], + * // ['blue', 'small', 'slow'] + * // ] * ``` */ export function product(arrays: T[][]): T[][] { From b070d3e9f3daa4d1c63bf9fefba5413161e150d8 Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Wed, 11 Sep 2024 23:23:01 +0500 Subject: [PATCH 04/11] test: fix tests Co-authored-by: Marlon Passos <1marlonpassos@gmail.com> --- tests/array/product.test.ts | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/tests/array/product.test.ts b/tests/array/product.test.ts index a29fbc1f..88b2323f 100644 --- a/tests/array/product.test.ts +++ b/tests/array/product.test.ts @@ -1,27 +1,24 @@ import * as _ from 'radashi' describe('product', () => { - test('returns an array containing an empty array when given an empty input array (n=0)', () => { - expect(_.product([])).toEqual([[]]) - }) test('returns an empty array when given an array containing an empty array (n=1)', () => { - expect(_.product([[]])).toEqual([]) + expect(_.product([])).toEqual([]) }) test('returns an empty array when given multiple empty arrays (n>1)', () => { - expect(_.product([[], [], []])).toEqual([]) + expect(_.product([], [], [])).toEqual([]) }) test('returns an empty array when one of the arrays in the input is empty (n>1)', () => { - expect(_.product([['1', '2', '3'], []])).toEqual([]) + expect(_.product(['1', '2', '3'], [])).toEqual([]) }) test('returns an array of singletons when given a single array (n=1)', () => { - expect(_.product([['1', '2', '3']])).toEqual([['1'], ['2'], ['3']]) + expect(_.product(['1', '2', '3'])).toEqual([['1'], ['2'], ['3']]) }) test('performs a correct Cartesian product for two arrays (n=2)', () => { expect( - _.product([ + _.product( ['red', 'blue'], ['fast', 'slow'], - ]), + ), ).toEqual([ ['red', 'fast'], ['red', 'slow'], @@ -31,11 +28,11 @@ describe('product', () => { }) test('performs a correct Cartesian product for more than two arrays (n>2)', () => { expect( - _.product([ + _.product( ['red', 'blue'], ['fast', 'slow'], ['big', 'small'], - ]), + ), ).toEqual([ ['red', 'fast', 'big'], ['red', 'fast', 'small'], @@ -45,6 +42,6 @@ describe('product', () => { ['blue', 'fast', 'small'], ['blue', 'slow', 'big'], ['blue', 'slow', 'small'], - ]) + ]) }) }) From dfd0ebf73363ada6ea32bbdfd7aacb7d880f2715 Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Wed, 11 Sep 2024 23:23:55 +0500 Subject: [PATCH 05/11] fix: types + Also use `castArray` instead of `.slice()` for shallow copy --- src/array/product.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/array/product.ts b/src/array/product.ts index cbf7f3c1..cc4aadf6 100644 --- a/src/array/product.ts +++ b/src/array/product.ts @@ -1,5 +1,7 @@ +import { castArray } from 'radashi' + /** - * Creates an [n-ary Cartesian product](https://en.wikipedia.org/wiki/Cartesian_product#n-ary_Cartesian_product) from the given arrays. + * Create an [n-ary Cartesian product](https://en.wikipedia.org/wiki/Cartesian_product#n-ary_Cartesian_product) from the given arrays. * * @see https://radashi.js.org/reference/array/product * @example @@ -17,13 +19,16 @@ * // ] * ``` */ -export function product(arrays: T[][]): T[][] { +export function product( + ...arrays: [...T] +): Array<{ [K in keyof T]: T[K][number] }> +export function product(...arrays: T): T[][] { let out: T[][] = [[]] for (const array of arrays) { - const result: T[][] = [] + const result = [] for (const currentArray of out) { for (const item of array) { - const currentArrayCopy = currentArray.slice() + const currentArrayCopy = castArray(currentArray) currentArrayCopy.push(item) result.push(currentArrayCopy) } From a02255ddaf30425a94436ec4478abd6dcc24b412 Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Thu, 12 Sep 2024 00:03:46 +0500 Subject: [PATCH 06/11] fix: rename to `cartesianProduct` + Add type tests + Update tests to support multiple types + Update doc to display multiple types --- ...uct.bench.ts => cartesianProduct.bench.ts} | 12 +-- docs/array/cartesianProduct.mdx | 35 ++++++++ docs/array/product.mdx | 31 ------- src/array/{product.ts => cartesianProduct.ts} | 8 +- src/mod.ts | 2 +- tests/array/cartesianProduct.test.ts | 87 +++++++++++++++++++ tests/array/product.test.ts | 47 ---------- 7 files changed, 132 insertions(+), 90 deletions(-) rename benchmarks/array/{product.bench.ts => cartesianProduct.bench.ts} (69%) create mode 100644 docs/array/cartesianProduct.mdx delete mode 100644 docs/array/product.mdx rename src/array/{product.ts => cartesianProduct.ts} (81%) create mode 100644 tests/array/cartesianProduct.test.ts delete mode 100644 tests/array/product.test.ts diff --git a/benchmarks/array/product.bench.ts b/benchmarks/array/cartesianProduct.bench.ts similarity index 69% rename from benchmarks/array/product.bench.ts rename to benchmarks/array/cartesianProduct.bench.ts index 75db3c6f..568400d1 100644 --- a/benchmarks/array/product.bench.ts +++ b/benchmarks/array/cartesianProduct.bench.ts @@ -1,27 +1,27 @@ import * as _ from 'radashi' -describe('product', () => { +describe('cartesianProduct', () => { bench('with no arguments', () => { - _.product([]) + _.cartesianProduct([]) }) bench('with single empty array', () => { - _.product([[]]) + _.cartesianProduct([[]]) }) bench('with one non-empty array (n=1)', () => { - _.product([['a', 'b', 'c']]) + _.cartesianProduct([['a', 'b', 'c']]) }) bench('with two small arrays (n=2)', () => { - _.product([ + _.cartesianProduct([ ['red', 'blue'], ['fast', 'slow'], ]) }) bench('with three small arrays (n=3)', () => { - _.product([ + _.cartesianProduct([ ['red', 'blue'], ['fast', 'slow'], ['big', 'small'], diff --git a/docs/array/cartesianProduct.mdx b/docs/array/cartesianProduct.mdx new file mode 100644 index 00000000..75250464 --- /dev/null +++ b/docs/array/cartesianProduct.mdx @@ -0,0 +1,35 @@ +--- +title: cartesianProduct +description: Perform a Cartesian product of arrays +--- + +### Usage + +Create an [n-ary Cartesian product](https://en.wikipedia.org/wiki/Cartesian_product#n-ary_Cartesian_product) from the given arrays. +The inputs are arrays, and the output is an array of arrays representing all +possible combinations where the first element is from the first array, the second element +is from the second array, and so on. + +```ts +import * as _ from 'radashi' + +const colors = ['red', 'blue'] +const numbers = [1, 2, 3] +const booleans = [true, false] + +_.cartesianProduct(colors, numbers, booleans) +// => [ +// ['red', 1, true], +// ['red', 1, false], +// ['red', 2, true], +// ['red', 2, false], +// ['red', 3, true], +// ['red', 3, false], +// ['blue', 1, true], +// ['blue', 1, false], +// ['blue', 2, true], +// ['blue', 2, false], +// ['blue', 3, true], +// ['blue', 3, false], +// ] +``` diff --git a/docs/array/product.mdx b/docs/array/product.mdx deleted file mode 100644 index 77ea5c60..00000000 --- a/docs/array/product.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: product -description: Perform a Cartesian product of arrays ---- - -### Usage - -Creates an [n-ary Cartesian product](https://en.wikipedia.org/wiki/Cartesian_product#n-ary_Cartesian_product) from the given arrays. -The input is an array of arrays, and the output is an array of arrays representing all -possible combinations where the first element is from the first array, the second element -is from the second array, and so on. - -```ts -import * as _ from 'radashi' - -const colors = ['red', 'blue'] -const sizes = ['big', 'small'] -const compliments = ['nice', 'great'] - -_.product([colors, sizes, compliments]) -// => [ -// ['red', 'big', 'nice'], -// ['red', 'big', 'great'], -// ['red', 'small', 'nice'], -// ['red', 'small', 'great'], -// ['blue', 'big', 'nice'], -// ['blue', 'big', 'great'], -// ['blue', 'small', 'nice'], -// ['blue', 'small', 'great'] -// ] -``` diff --git a/src/array/product.ts b/src/array/cartesianProduct.ts similarity index 81% rename from src/array/product.ts rename to src/array/cartesianProduct.ts index cc4aadf6..d0075541 100644 --- a/src/array/product.ts +++ b/src/array/cartesianProduct.ts @@ -1,5 +1,3 @@ -import { castArray } from 'radashi' - /** * Create an [n-ary Cartesian product](https://en.wikipedia.org/wiki/Cartesian_product#n-ary_Cartesian_product) from the given arrays. * @@ -19,16 +17,16 @@ import { castArray } from 'radashi' * // ] * ``` */ -export function product( +export function cartesianProduct( ...arrays: [...T] ): Array<{ [K in keyof T]: T[K][number] }> -export function product(...arrays: T): T[][] { +export function cartesianProduct(...arrays: T): T[][] { let out: T[][] = [[]] for (const array of arrays) { const result = [] for (const currentArray of out) { for (const item of array) { - const currentArrayCopy = castArray(currentArray) + const currentArrayCopy = currentArray.slice() currentArrayCopy.push(item) result.push(currentArrayCopy) } diff --git a/src/mod.ts b/src/mod.ts index c760143b..098427d4 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -1,5 +1,6 @@ export * from './array/alphabetical.ts' export * from './array/boil.ts' +export * from './array/cartesianProduct.ts' export * from './array/castArray.ts' export * from './array/castArrayIfExists.ts' export * from './array/cluster.ts' @@ -16,7 +17,6 @@ export * from './array/list.ts' export * from './array/mapify.ts' export * from './array/merge.ts' export * from './array/objectify.ts' -export * from './array/product.ts' export * from './array/replace.ts' export * from './array/replaceOrAppend.ts' export * from './array/select.ts' diff --git a/tests/array/cartesianProduct.test.ts b/tests/array/cartesianProduct.test.ts new file mode 100644 index 00000000..40bed87b --- /dev/null +++ b/tests/array/cartesianProduct.test.ts @@ -0,0 +1,87 @@ +import * as _ from 'radashi' + +describe('cartesianProduct', () => { + test('returns an empty array when given an array containing an empty array (n=1)', () => { + expect(_.cartesianProduct([])).toEqual([]) + }) + test('returns an empty array when given multiple empty arrays (n>1)', () => { + expect(_.cartesianProduct([], [], [])).toEqual([]) + }) + test('returns an empty array when one of the arrays in the input is empty (n>1)', () => { + expect(_.cartesianProduct(['1', '2', '3'], [])).toEqual([]) + }) + test('returns an array of singletons when given a single array (n=1)', () => { + expect(_.cartesianProduct(['1', '2', '3'])).toEqual([['1'], ['2'], ['3']]) + }) + test('performs a correct Cartesian cartesianProduct for two arrays (n=2)', () => { + expect(_.cartesianProduct(['red', 'blue'], [1, 2, 3])).toEqual([ + ['red', 1], + ['red', 2], + ['red', 3], + ['blue', 1], + ['blue', 2], + ['blue', 3], + ]) + }) + test('performs a correct Cartesian cartesianProduct for more than two arrays (n>2)', () => { + expect( + _.cartesianProduct(['red', 'blue'], [1, 2, 3], [true, false]), + ).toEqual([ + ['red', 1, true], + ['red', 1, false], + ['red', 2, true], + ['red', 2, false], + ['red', 3, true], + ['red', 3, false], + ['blue', 1, true], + ['blue', 1, false], + ['blue', 2, true], + ['blue', 2, false], + ['blue', 3, true], + ['blue', 3, false], + ]) + }) +}) + +describe('cartesianProduct types', () => { + test('with an empty array', () => { + const result = _.cartesianProduct([]) + expectTypeOf(result).toEqualTypeOf<[never][]>() + }) + test('with two arrays of the same type', () => { + const result = _.cartesianProduct(['red', 'blue'], ['fast', 'slow']) + const [[v1, v2]] = result + expectTypeOf(result).toEqualTypeOf<[string, string][]>() + expectTypeOf(v1).toEqualTypeOf() + expectTypeOf(v2).toEqualTypeOf() + }) + test('with two arrays of different types', () => { + const result = _.cartesianProduct(['red', 'blue'], [1, 2]) + const [[v1, v2]] = result + expectTypeOf(result).toEqualTypeOf<[string, number][]>() + expectTypeOf(v1).toEqualTypeOf() + expectTypeOf(v2).toEqualTypeOf() + }) + test('with three arrays of different types', () => { + const result = _.cartesianProduct(['red', 'blue'], [1, 2], [true, false]) + const [[v1, v2, v3]] = result + expectTypeOf(result).toEqualTypeOf<[string, number, boolean][]>() + expectTypeOf(v1).toEqualTypeOf() + expectTypeOf(v2).toEqualTypeOf() + expectTypeOf(v3).toEqualTypeOf() + }) + test('with constant arrays of different types', () => { + const result = _.cartesianProduct(['red', 'blue'] as const, [1, 2] as const) + const [[v1, v2]] = result + expectTypeOf(result).toEqualTypeOf<['red' | 'blue', 1 | 2][]>() + expectTypeOf(v1).toEqualTypeOf<'red' | 'blue'>() + expectTypeOf(v2).toEqualTypeOf<1 | 2>() + }) + test('with a mix of constant and non-constant types', () => { + const result = _.cartesianProduct(['red', 'blue'], [1, 2] as const) + const [[v1, v2]] = result + expectTypeOf(result).toEqualTypeOf<[string, 1 | 2][]>() + expectTypeOf(v1).toEqualTypeOf() + expectTypeOf(v2).toEqualTypeOf<1 | 2>() + }) +}) diff --git a/tests/array/product.test.ts b/tests/array/product.test.ts deleted file mode 100644 index 88b2323f..00000000 --- a/tests/array/product.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import * as _ from 'radashi' - -describe('product', () => { - test('returns an empty array when given an array containing an empty array (n=1)', () => { - expect(_.product([])).toEqual([]) - }) - test('returns an empty array when given multiple empty arrays (n>1)', () => { - expect(_.product([], [], [])).toEqual([]) - }) - test('returns an empty array when one of the arrays in the input is empty (n>1)', () => { - expect(_.product(['1', '2', '3'], [])).toEqual([]) - }) - test('returns an array of singletons when given a single array (n=1)', () => { - expect(_.product(['1', '2', '3'])).toEqual([['1'], ['2'], ['3']]) - }) - test('performs a correct Cartesian product for two arrays (n=2)', () => { - expect( - _.product( - ['red', 'blue'], - ['fast', 'slow'], - ), - ).toEqual([ - ['red', 'fast'], - ['red', 'slow'], - ['blue', 'fast'], - ['blue', 'slow'], - ]) - }) - test('performs a correct Cartesian product for more than two arrays (n>2)', () => { - expect( - _.product( - ['red', 'blue'], - ['fast', 'slow'], - ['big', 'small'], - ), - ).toEqual([ - ['red', 'fast', 'big'], - ['red', 'fast', 'small'], - ['red', 'slow', 'big'], - ['red', 'slow', 'small'], - ['blue', 'fast', 'big'], - ['blue', 'fast', 'small'], - ['blue', 'slow', 'big'], - ['blue', 'slow', 'small'], - ]) - }) -}) From 7343ee6fa2d45a4354d85a2a06c3a033c01c59c9 Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Thu, 12 Sep 2024 00:09:36 +0500 Subject: [PATCH 07/11] test: fix n=0 test --- tests/array/cartesianProduct.test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/array/cartesianProduct.test.ts b/tests/array/cartesianProduct.test.ts index 40bed87b..a1698951 100644 --- a/tests/array/cartesianProduct.test.ts +++ b/tests/array/cartesianProduct.test.ts @@ -1,7 +1,10 @@ import * as _ from 'radashi' describe('cartesianProduct', () => { - test('returns an empty array when given an array containing an empty array (n=1)', () => { + test('returns an array containing an empty array when given an empty input (n=0)', () => { + expect(_.cartesianProduct()).toEqual([[]]) + }) + test('returns an empty array when given an empty array (n=1)', () => { expect(_.cartesianProduct([])).toEqual([]) }) test('returns an empty array when given multiple empty arrays (n>1)', () => { From 64e0b6bd14c96400ad7a20956642ac3f2a816a3f Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Thu, 12 Sep 2024 00:10:16 +0500 Subject: [PATCH 08/11] fix: benchmarks to use spread input --- benchmarks/array/cartesianProduct.bench.ts | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/benchmarks/array/cartesianProduct.bench.ts b/benchmarks/array/cartesianProduct.bench.ts index 568400d1..7386aa06 100644 --- a/benchmarks/array/cartesianProduct.bench.ts +++ b/benchmarks/array/cartesianProduct.bench.ts @@ -2,29 +2,22 @@ import * as _ from 'radashi' describe('cartesianProduct', () => { bench('with no arguments', () => { - _.cartesianProduct([]) + _.cartesianProduct() }) bench('with single empty array', () => { - _.cartesianProduct([[]]) + _.cartesianProduct([]) }) bench('with one non-empty array (n=1)', () => { - _.cartesianProduct([['a', 'b', 'c']]) + _.cartesianProduct(['a', 'b', 'c']) }) bench('with two small arrays (n=2)', () => { - _.cartesianProduct([ - ['red', 'blue'], - ['fast', 'slow'], - ]) + _.cartesianProduct(['red', 'blue'], ['fast', 'slow']) }) bench('with three small arrays (n=3)', () => { - _.cartesianProduct([ - ['red', 'blue'], - ['fast', 'slow'], - ['big', 'small'], - ]) + _.cartesianProduct(['red', 'blue'], ['fast', 'slow'], ['big', 'small']) }) }) From d0fafa82a39960601f6bc377c78e9cf8755227a9 Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Fri, 13 Sep 2024 14:07:33 +0500 Subject: [PATCH 09/11] fix: code review notes --- benchmarks/array/cartesianProduct.bench.ts | 8 +--- src/mod.ts | 1 + tests/array/cartesianProduct.test-d.ts | 44 ++++++++++++++++++++++ tests/array/cartesianProduct.test.ts | 43 --------------------- 4 files changed, 47 insertions(+), 49 deletions(-) create mode 100644 tests/array/cartesianProduct.test-d.ts diff --git a/benchmarks/array/cartesianProduct.bench.ts b/benchmarks/array/cartesianProduct.bench.ts index 7386aa06..37b36ec5 100644 --- a/benchmarks/array/cartesianProduct.bench.ts +++ b/benchmarks/array/cartesianProduct.bench.ts @@ -1,15 +1,11 @@ import * as _ from 'radashi' describe('cartesianProduct', () => { - bench('with no arguments', () => { - _.cartesianProduct() - }) - - bench('with single empty array', () => { + bench('with an empty array (n=1)', () => { _.cartesianProduct([]) }) - bench('with one non-empty array (n=1)', () => { + bench('with a non-empty array (n=1)', () => { _.cartesianProduct(['a', 'b', 'c']) }) diff --git a/src/mod.ts b/src/mod.ts index 098427d4..53b9ff56 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -29,6 +29,7 @@ export * from './array/unique.ts' export * from './array/unzip.ts' export * from './array/zip.ts' export * from './array/zipToObject.ts' + export * from './async/AggregateError.ts' export * from './async/all.ts' export * from './async/defer.ts' diff --git a/tests/array/cartesianProduct.test-d.ts b/tests/array/cartesianProduct.test-d.ts new file mode 100644 index 00000000..2a3bc5dd --- /dev/null +++ b/tests/array/cartesianProduct.test-d.ts @@ -0,0 +1,44 @@ +import * as _ from 'radashi' + +describe('cartesianProduct return type', () => { + test('with an empty array', () => { + const result = _.cartesianProduct([]) + expectTypeOf(result).toEqualTypeOf<[never][]>() + }) + test('with two arrays of the same type', () => { + const result = _.cartesianProduct(['red', 'blue'], ['fast', 'slow']) + const [[v1, v2]] = result + expectTypeOf(result).toEqualTypeOf<[string, string][]>() + expectTypeOf(v1).toEqualTypeOf() + expectTypeOf(v2).toEqualTypeOf() + }) + test('with two arrays of different types', () => { + const result = _.cartesianProduct(['red', 'blue'], [1, 2]) + const [[v1, v2]] = result + expectTypeOf(result).toEqualTypeOf<[string, number][]>() + expectTypeOf(v1).toEqualTypeOf() + expectTypeOf(v2).toEqualTypeOf() + }) + test('with three arrays of different types', () => { + const result = _.cartesianProduct(['red', 'blue'], [1, 2], [true, false]) + const [[v1, v2, v3]] = result + expectTypeOf(result).toEqualTypeOf<[string, number, boolean][]>() + expectTypeOf(v1).toEqualTypeOf() + expectTypeOf(v2).toEqualTypeOf() + expectTypeOf(v3).toEqualTypeOf() + }) + test('with constant arrays of different types', () => { + const result = _.cartesianProduct(['red', 'blue'] as const, [1, 2] as const) + const [[v1, v2]] = result + expectTypeOf(result).toEqualTypeOf<['red' | 'blue', 1 | 2][]>() + expectTypeOf(v1).toEqualTypeOf<'red' | 'blue'>() + expectTypeOf(v2).toEqualTypeOf<1 | 2>() + }) + test('with a mix of constant and non-constant types', () => { + const result = _.cartesianProduct(['red', 'blue'], [1, 2] as const) + const [[v1, v2]] = result + expectTypeOf(result).toEqualTypeOf<[string, 1 | 2][]>() + expectTypeOf(v1).toEqualTypeOf() + expectTypeOf(v2).toEqualTypeOf<1 | 2>() + }) +}) diff --git a/tests/array/cartesianProduct.test.ts b/tests/array/cartesianProduct.test.ts index a1698951..10c0d655 100644 --- a/tests/array/cartesianProduct.test.ts +++ b/tests/array/cartesianProduct.test.ts @@ -45,46 +45,3 @@ describe('cartesianProduct', () => { ]) }) }) - -describe('cartesianProduct types', () => { - test('with an empty array', () => { - const result = _.cartesianProduct([]) - expectTypeOf(result).toEqualTypeOf<[never][]>() - }) - test('with two arrays of the same type', () => { - const result = _.cartesianProduct(['red', 'blue'], ['fast', 'slow']) - const [[v1, v2]] = result - expectTypeOf(result).toEqualTypeOf<[string, string][]>() - expectTypeOf(v1).toEqualTypeOf() - expectTypeOf(v2).toEqualTypeOf() - }) - test('with two arrays of different types', () => { - const result = _.cartesianProduct(['red', 'blue'], [1, 2]) - const [[v1, v2]] = result - expectTypeOf(result).toEqualTypeOf<[string, number][]>() - expectTypeOf(v1).toEqualTypeOf() - expectTypeOf(v2).toEqualTypeOf() - }) - test('with three arrays of different types', () => { - const result = _.cartesianProduct(['red', 'blue'], [1, 2], [true, false]) - const [[v1, v2, v3]] = result - expectTypeOf(result).toEqualTypeOf<[string, number, boolean][]>() - expectTypeOf(v1).toEqualTypeOf() - expectTypeOf(v2).toEqualTypeOf() - expectTypeOf(v3).toEqualTypeOf() - }) - test('with constant arrays of different types', () => { - const result = _.cartesianProduct(['red', 'blue'] as const, [1, 2] as const) - const [[v1, v2]] = result - expectTypeOf(result).toEqualTypeOf<['red' | 'blue', 1 | 2][]>() - expectTypeOf(v1).toEqualTypeOf<'red' | 'blue'>() - expectTypeOf(v2).toEqualTypeOf<1 | 2>() - }) - test('with a mix of constant and non-constant types', () => { - const result = _.cartesianProduct(['red', 'blue'], [1, 2] as const) - const [[v1, v2]] = result - expectTypeOf(result).toEqualTypeOf<[string, 1 | 2][]>() - expectTypeOf(v1).toEqualTypeOf() - expectTypeOf(v2).toEqualTypeOf<1 | 2>() - }) -}) From 04e44e85cadbbe82b6eb734d31541ecd66d3757d Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Fri, 13 Sep 2024 14:36:11 +0500 Subject: [PATCH 10/11] fix: input should be strictly an array of arrays --- src/array/cartesianProduct.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/array/cartesianProduct.ts b/src/array/cartesianProduct.ts index d0075541..63981601 100644 --- a/src/array/cartesianProduct.ts +++ b/src/array/cartesianProduct.ts @@ -17,7 +17,7 @@ * // ] * ``` */ -export function cartesianProduct( +export function cartesianProduct( ...arrays: [...T] ): Array<{ [K in keyof T]: T[K][number] }> export function cartesianProduct(...arrays: T): T[][] { From 13f6d547cec3282d0dc981a5177377c61486ba38 Mon Sep 17 00:00:00 2001 From: Alec Larson <1925840+aleclarson@users.noreply.github.com> Date: Sat, 14 Sep 2024 15:14:14 -0400 Subject: [PATCH 11/11] Update src/array/cartesianProduct.ts --- src/array/cartesianProduct.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/array/cartesianProduct.ts b/src/array/cartesianProduct.ts index 63981601..162ff0d7 100644 --- a/src/array/cartesianProduct.ts +++ b/src/array/cartesianProduct.ts @@ -1,7 +1,7 @@ /** * Create an [n-ary Cartesian product](https://en.wikipedia.org/wiki/Cartesian_product#n-ary_Cartesian_product) from the given arrays. * - * @see https://radashi.js.org/reference/array/product + * @see https://radashi.js.org/reference/array/cartesianProduct * @example * ```ts * product([['red', 'blue'], ['big', 'small'], ['fast', 'slow']])