From e09f26d196386e689c4abf00b44ba7d88d05a522 Mon Sep 17 00:00:00 2001 From: --replace-all Date: Thu, 15 Feb 2024 19:41:14 -0500 Subject: [PATCH] feat: add product function --- docs/array/product.mdx | 31 +++++++++++++++++++++++ src/array.ts | 19 ++++++++++++++ src/index.ts | 2 ++ src/tests/array.test.ts | 56 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+) create mode 100644 docs/array/product.mdx diff --git a/docs/array/product.mdx b/docs/array/product.mdx new file mode 100644 index 00000000..75152884 --- /dev/null +++ b/docs/array/product.mdx @@ -0,0 +1,31 @@ +--- +title: product +group: 'Array' +description: Get Cartesian product of the arguments +--- + +## Basic usage + +```ts +import { product } from 'radash' + +const array1 = [0, 1] +const array2 = ['a', 'b'] +const array3 = ['A', 'B', 'C'] + +product(array1, array2, array3) /* => [ + [0, 'a', 'A'], + [0, 'a', 'B'], + [0, 'a', 'C'], + [0, 'b', 'A'], + [0, 'b', 'B'], + [0, 'b', 'C'], + [1, 'a', 'A'], + [1, 'a', 'B'], + [1, 'a', 'C'], + [1, 'b', 'A'], + [1, 'b', 'B'], + [1, 'b', 'C'], +] +*/ +``` diff --git a/src/array.ts b/src/array.ts index 60db772e..f2e0a6a9 100644 --- a/src/array.ts +++ b/src/array.ts @@ -548,3 +548,22 @@ export function shift(arr: Array, n: number) { return [...arr.slice(-shiftNumber, arr.length), ...arr.slice(0, -shiftNumber)] } + +export type ProductItem = Arrs extends [Array, ...infer Rest] ? Rest extends [] ? [T] : [T, ...ProductItem] : never; +export function product[]>(...args: T): Array> { + if (args.some(arg => arg.length < 1)) return []; + const indices = new Array(args.length).fill(0); + let currentIndex = args.length - 1; + const result = [] as Array; + while (indices[0] < args[0]?.length) { + result.push(indices.map((j, i) => args[i][j])); + indices[currentIndex] += 1; + while (currentIndex > 0 && indices[currentIndex] === args[currentIndex].length) { + indices[currentIndex] = 0; + indices[currentIndex - 1] += 1; + currentIndex -= 1; + } + currentIndex = args.length - 1; + } + return result; +} diff --git a/src/index.ts b/src/index.ts index 6c0f6068..862a9539 100644 --- a/src/index.ts +++ b/src/index.ts @@ -16,6 +16,8 @@ export { merge, min, objectify, + product, + ProductItem, range, replace, replaceOrAppend, diff --git a/src/tests/array.test.ts b/src/tests/array.test.ts index 92a177c2..538a9aea 100644 --- a/src/tests/array.test.ts +++ b/src/tests/array.test.ts @@ -777,4 +777,60 @@ describe('array module', () => { assert.deepEqual(result, ['b', 'a']) }) }) + + describe('product function', () => { + const arrA = [{}, {}, {}] + const arrB = [{}, {}] + const arrC = [{}] + test('returns cartesian product of two arguments', () => { + const result = _.product(arrA, arrB) + assert.deepEqual(result, [ + [arrA[0], arrB[0]], + [arrA[0], arrB[1]], + [arrA[1], arrB[0]], + [arrA[1], arrB[1]], + [arrA[2], arrB[0]], + [arrA[2], arrB[1]], + ]) + }) + test('returns cartesian product of more than two arguments', () => { + const result = _.product(arrB, arrB, arrB) + assert.deepEqual(result, [ + [arrB[0], arrB[0], arrB[0]], + [arrB[0], arrB[0], arrB[1]], + [arrB[0], arrB[1], arrB[0]], + [arrB[0], arrB[1], arrB[1]], + [arrB[1], arrB[0], arrB[0]], + [arrB[1], arrB[0], arrB[1]], + [arrB[1], arrB[1], arrB[0]], + [arrB[1], arrB[1], arrB[1]], + ]) + }) + test('works when one of the arguments has 1 item', () => { + const result = _.product(arrB, arrC, arrB) + assert.deepEqual(result, [ + [arrB[0], arrC[0], arrB[0]], + [arrB[0], arrC[0], arrB[1]], + [arrB[1], arrC[0], arrB[0]], + [arrB[1], arrC[0], arrB[1]], + ]) + }) + test('works when called with a single argument', () => { + const result = _.product(arrA) + assert.deepEqual(result, [ + [arrA[0]], + [arrA[1]], + [arrA[2]], + ]) + }) + test('returns empty array when one of the arguments is an empty array', () => { + const result = _.product(arrA, [], arrB) + assert.deepEqual(result, []) + }) + test('returns empty array when called with no arguments', () => { + const result = _.product() + assert.deepEqual(result, []) + }) + }) + })