Декартово произведение множественных массивов в JavaScript
Как бы вы реализовали декартово произведение множественных массивов в JavaScript?
В качестве примера,
cartesian([1,2],[10,20],[100,200,300]) //should be
// [[1,10,100],[1,10,200],[1,10,300],[2,10,100],[2,10,200]...]
37 ответов
Вот функциональное решение проблемы (без изменяемой переменной!) С использованием reduce
а также flatten
предоставлено underscore.js
:
function cartesianProductOf() {
return _.reduce(arguments, function(a, b) {
return _.flatten(_.map(a, function(x) {
return _.map(b, function(y) {
return x.concat([y]);
});
}), true);
}, [ [] ]);
};
cartesianProductOf([1, 2], [3, 4], ['a', 'b']); // [[1,3,"a"],[1,3,"b"],[1,4,"a"],[1,4,"b"],[2,3,"a"],[2,3,"b"],[2,4,"a"],[2,4,"b"]]
Примечание: это решение было разработано по http://cwestblog.com/2011/05/02/cartesian-product-of-multiple-arrays/
2017 Обновление: 2-строчный ответ с ванильным JS
Все ответы здесь слишком сложны, большинство из них занимают 20 строк кода или даже больше.
В этом примере используются только две строки ванильного JavaScript, без lodash, подчеркивания или других библиотек:
let f = (a, b) => [].concat(...a.map(a => b.map(b => [].concat(a, b))));
let cartesian = (a, b, ...c) => b ? cartesian(f(a, b), ...c) : a;
Обновить:
Это то же самое, что и выше, но улучшено в строгом соответствии с Руководством по стилю JavaScript для Airbnb - проверено с использованием ESLint с https://www.npmjs.com/package/eslint-config-airbnb-base:
const f = (a, b) => [].concat(...a.map(d => b.map(e => [].concat(d, e))));
const cartesian = (a, b, ...c) => (b ? cartesian(f(a, b), ...c) : a);
Отдельное спасибо ZuBB за сообщение о проблемах линтера с оригинальным кодом.
пример
Это точный пример из вашего вопроса:
let output = cartesian([1,2],[10,20],[100,200,300]);
Выход
Это результат этой команды:
[ [ 1, 10, 100 ],
[ 1, 10, 200 ],
[ 1, 10, 300 ],
[ 1, 20, 100 ],
[ 1, 20, 200 ],
[ 1, 20, 300 ],
[ 2, 10, 100 ],
[ 2, 10, 200 ],
[ 2, 10, 300 ],
[ 2, 20, 100 ],
[ 2, 20, 200 ],
[ 2, 20, 300 ] ]
демонстрация
Смотрите демоверсии на:
- JS Bin с Babel (для старых браузеров)
- JS Bin без Babel (для современных браузеров)
Синтаксис
Синтаксис, который я использовал здесь, не является чем-то новым. В моем примере используется оператор распространения и остальные параметры - функции JavaScript, определенные в 6-й редакции стандарта ECMA-262, опубликованной в июне 2015 года и разработанной намного раньше, более известной как ES6 или ES2015. Увидеть:
- http://www.ecma-international.org/ecma-262/6.0/
- https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/rest_parameters
- https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Spread_operator
Это делает такой код настолько простым, что грех не использовать его. Для старых платформ, которые не поддерживают его изначально, вы всегда можете использовать Babel или другие инструменты, чтобы перенести его в более старый синтаксис - и фактически мой пример, переданный Babel, все еще короче и проще, чем большинство примеров здесь, но это не так действительно важно, потому что вывод транспиляции - это не то, что вам нужно понимать или поддерживать, это просто факт, который мне показался интересным.
Заключение
Нет необходимости писать сотни строк кода, которые сложно поддерживать, и нет необходимости использовать целые библиотеки для такой простой вещи, когда две строки ванильного JavaScript могут легко выполнить свою работу. Как вы можете видеть, использование современных функций языка действительно окупается, и в тех случаях, когда вам необходимо поддерживать архаичные платформы без встроенной поддержки современных функций, вы всегда можете использовать Babel или другие инструменты для переноса нового синтаксиса в старый.,
Не используйте код, как сейчас 1995
JavaScript развивается, и это происходит по причине. TC39 делает потрясающую работу над языковым дизайном с добавлением новых функций, а производители браузеров делают потрясающую работу по реализации этих функций.
Чтобы увидеть текущее состояние встроенной поддержки любой данной функции в браузерах, см.:
Чтобы увидеть поддержку в версиях Node, смотрите:
Чтобы использовать современный синтаксис на платформах, которые его не поддерживают, используйте Babel:
Вот модифицированная версия кода @viebel в простом Javascript, без использования какой-либо библиотеки:
function cartesianProduct(arr)
{
return arr.reduce(function(a,b){
return a.map(function(x){
return b.map(function(y){
return x.concat(y);
})
}).reduce(function(a,b){ return a.concat(b) },[])
}, [[]])
}
var a = cartesianProduct([[1, 2,3], [4, 5,6], [7, 8], [9,10]]);
console.log(a);
Следующая эффективная функция генератора возвращает декартово произведение всех заданных итераций:
// Generate cartesian product of given iterables:
function* cartesian(head, ...tail) {
const remainder = tail.length > 0 ? cartesian(...tail) : [[]];
for (let r of remainder) for (let h of head) yield [h, ...r];
}
// Example:
console.log(...cartesian([1, 2], [10, 20], [100, 200, 300]));
Он принимает массивы, строки, наборы и все другие объекты, реализующие итерируемый протокол.
Следуя спецификации n-арного декартового произведения, оно дает
[]
если одна или несколько заданных итераций пусты, например,[]
или же''
[[a]]
если одна итерация, содержащая одно значениеa
дано.
Все остальные случаи обрабатываются как ожидалось, как продемонстрировано в следующих тестовых случаях:
// Generate cartesian product of given iterables:
function* cartesian(head, ...tail) {
const remainder = tail.length > 0 ? cartesian(...tail) : [[]];
for (let r of remainder) for (let h of head) yield [h, ...r];
}
// Test cases:
console.log([...cartesian([])]); // []
console.log([...cartesian([1])]); // [[1]]
console.log([...cartesian([1, 2])]); // [[1], [2]]
console.log([...cartesian([1], [])]); // []
console.log([...cartesian([1, 2], [])]); // []
console.log([...cartesian([1], [2])]); // [[1, 2]]
console.log([...cartesian([1], [2], [3])]); // [[1, 2, 3]]
console.log([...cartesian([1, 2], [3, 4])]); // [[1, 3], [2, 3], [1, 4], [2, 4]]
console.log([...cartesian('')]); // []
console.log([...cartesian('ab', 'c')]); // [['a','c'], ['b', 'c']]
console.log([...cartesian([1, 2], 'ab')]); // [[1, 'a'], [2, 'a'], [1, 'b'], [2, 'b']]
console.log([...cartesian(new Set())]); // []
console.log([...cartesian(new Set([1]))]); // [[1]]
console.log([...cartesian(new Set([1, 1]))]); // [[1]]
Кажется, что сообщество думает, что это тривиально и / или легко найти эталонную реализацию, после краткого осмотра я не смог или, может быть, просто мне нравится заново изобретать колесо или решать задачи программирования, подобные классной комнате, так или иначе, это ваш счастливый день:
function cartProd(paramArray) {
function addTo(curr, args) {
var i, copy,
rest = args.slice(1),
last = !rest.length,
result = [];
for (i = 0; i < args[0].length; i++) {
copy = curr.slice();
copy.push(args[0][i]);
if (last) {
result.push(copy);
} else {
result = result.concat(addTo(copy, rest));
}
}
return result;
}
return addTo([], Array.prototype.slice.call(arguments));
}
>> console.log(cartProd([1,2], [10,20], [100,200,300]));
>> [
[1, 10, 100], [1, 10, 200], [1, 10, 300], [1, 20, 100],
[1, 20, 200], [1, 20, 300], [2, 10, 100], [2, 10, 200],
[2, 10, 300], [2, 20, 100], [2, 20, 200], [2, 20, 300]
]
полная справочная реализация, которая относительно эффективна...:-D
по эффективности: вы могли бы получить немного, вынув if из цикла и имея 2 отдельных цикла, так как он технически постоянен, и вы бы помогли с предсказанием ветвлений и всем этим беспорядком, но этот момент является спорным в javascript
в любом случае, наслаждайтесь -ck
Вот необычное, прямое рекурсивное решение:
function cartesianProduct(a) { // a = array of array
var i, j, l, m, a1, o = [];
if (!a || a.length == 0) return a;
a1 = a.splice(0, 1)[0]; // the first array of a
a = cartesianProduct(a);
for (i = 0, l = a1.length; i < l; i++) {
if (a && a.length) for (j = 0, m = a.length; j < m; j++)
o.push([a1[i]].concat(a[j]));
else
o.push([a1[i]]);
}
return o;
}
console.log(cartesianProduct([[1,2], [10,20], [100,200,300]]));
// [[1,10,100],[1,10,200],[1,10,300],[1,20,100],[1,20,200],[1,20,300],[2,10,100],[2,10,200],[2,10,300],[2,20,100],[2,20,200],[2,20,300]]
Вот однострочник с использованием собственного ES2019 flatMap
. Никаких библиотек не требуется, только современный браузер (или транспилятор):
data.reduce((a, b) => a.flatMap(x => b.map(y => [...x, y])), [[]]);
По сути, это современная версия ответа viebel без lodash.
Это помечено как функциональное программирование, поэтому давайте посмотрим на монаду List:
Одно из приложений для этого монадического списка представляет недетерминированные вычисления.
List
может содержать результаты для всех путей выполнения в алгоритме...
Ну, это звучит как идеально подходит для cartesian
, JavaScript дает нам Array
и функция монадического связывания Array.prototype.flatMap
так что давайте использовать их -
const cartesian = (...all) =>
{ const loop = (t, a, ...more) =>
a === undefined
? [ t ]
: a .flatMap (x => loop ([ ...t, x ], ...more))
return loop ([], ...all)
}
console .log (cartesian ([1,2], [10,20], [100,200,300]))
Вместо loop
выше, t
можно добавить в качестве параметра карри -
const makeCartesian = (t = []) => (a, ...more) =>
a === undefined
? [ t ]
: a .flatMap (x => makeCartesian ([ ...t, x ]) (...more))
const cartesian =
makeCartesian ()
console .log (cartesian ([1,2], [10,20], [100,200,300]))
Вот рекурсивный способ, который использует функцию генератора ECMAScript 2015, поэтому вам не нужно создавать все кортежи одновременно:
function* cartesian() {
let arrays = arguments;
function* doCartesian(i, prod) {
if (i == arrays.length) {
yield prod;
} else {
for (let j = 0; j < arrays[i].length; j++) {
yield* doCartesian(i + 1, prod.concat([arrays[i][j]]));
}
}
}
yield* doCartesian(0, []);
}
console.log(JSON.stringify(Array.from(cartesian([1,2],[10,20],[100,200,300]))));
console.log(JSON.stringify(Array.from(cartesian([[1],[2]],[10,20],[100,200,300]))));
Это чистое решение ES6 с использованием функций стрелок
function cartesianProduct(arr) {
return arr.reduce((a, b) =>
a.map(x => b.map(y => x.concat(y)))
.reduce((a, b) => a.concat(b), []), [[]]);
}
var arr = [[1, 2], [10, 20], [100, 200, 300]];
console.log(JSON.stringify(cartesianProduct(arr)));
Используя типичный возврат с генераторов ES6,
function cartesianProduct(...arrays) {
let current = new Array(arrays.length);
return (function* backtracking(index) {
if(index == arrays.length) yield current.slice();
else for(let num of arrays[index]) {
current[index] = num;
yield* backtracking(index+1);
}
})(0);
}
for (let item of cartesianProduct([1,2],[10,20],[100,200,300])) {
console.log('[' + item.join(', ') + ']');
}
div.as-console-wrapper { max-height: 100%; }
Ниже представлена аналогичная версия, совместимая со старыми браузерами.
function cartesianProduct(arrays) {
var result = [],
current = new Array(arrays.length);
(function backtracking(index) {
if(index == arrays.length) return result.push(current.slice());
for(var i=0; i<arrays[index].length; ++i) {
current[index] = arrays[index][i];
backtracking(index+1);
}
})(0);
return result;
}
cartesianProduct([[1,2],[10,20],[100,200,300]]).forEach(function(item) {
console.log('[' + item.join(', ') + ']');
});
div.as-console-wrapper { max-height: 100%; }
Кофейная версия с lodash:
_ = require("lodash")
cartesianProduct = ->
return _.reduceRight(arguments, (a,b) ->
_.flatten(_.map(a,(x) -> _.map b, (y) -> x.concat(y)), true)
, [ [] ])
Однострочный подход для лучшего чтения с отступами.
result = data.reduce(
(a, b) => a.reduce(
(r, v) => r.concat(b.map(w => [].concat(v, w))),
[]
)
);
Требуется один массив с массивами разыскиваемых декартовых элементов.
var data = [[1, 2], [10, 20], [100, 200, 300]],
result = data.reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []));
console.log(result.map(a => a.join(' ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Еще один, еще более упрощенный ответ в стиле 2021 года, использующий только методы reduce, map и concat:
Для тех, кому нужен TypeScript (переопределён ответ Дэнни)
/**
* Calculates "Cartesian Product" sets.
* @example
* cartesianProduct([[1,2], [4,8], [16,32]])
* Returns:
* [
* [1, 4, 16],
* [1, 4, 32],
* [1, 8, 16],
* [1, 8, 32],
* [2, 4, 16],
* [2, 4, 32],
* [2, 8, 16],
* [2, 8, 32]
* ]
* @see https://stackru.com/a/36234242/1955709
* @see https://en.wikipedia.org/wiki/Cartesian_product
* @param arr {T[][]}
* @returns {T[][]}
*/
function cartesianProduct<T> (arr: T[][]): T[][] {
return arr.reduce((a, b) => {
return a.map(x => {
return b.map(y => {
return x.concat(y)
})
}).reduce((c, d) => c.concat(d), [])
}, [[]] as T[][])
}
Современный JavaScript всего за несколько строк. Нет внешних библиотек или зависимостей, таких как Lodash.
function cartesian(...arrays) {
return arrays.reduce((a, b) => a.flatMap(x => b.map(y => x.concat([y]))), [ [] ]);
}
console.log(
cartesian([1, 2], [10, 20], [100, 200, 300])
.map(arr => JSON.stringify(arr))
.join('\n')
);
В моем конкретном случае "старомодный" подход казался более эффективным, чем методы, основанные на более современных функциях. Ниже приведен код (включая небольшое сравнение с другими решениями, опубликованными в этой теме @rsp и @sebnukem), если он окажется полезным для кого-то еще.
Идея следующая. Допустим, мы строим внешний продукт N
массивы, a_1,...,a_N
каждый из которых имеет m_i
компоненты. Внешний продукт этих массивов имеет M=m_1*m_2*...*m_N
элементы, и мы можем идентифицировать каждый из них с N-
мерный вектор, компоненты которого являются натуральными числами и i
-компонент строго ограничен сверху m_i
, Например, вектор (0, 0, ..., 0)
будет соответствовать конкретной комбинации, в которой каждый берет первый элемент из каждого массива, в то время как (m_1-1, m_2-1, ..., m_N-1)
идентифицируется с комбинацией, где каждый берет последний элемент из каждого массива. Таким образом, чтобы построить все M
В приведенных ниже комбинациях функция последовательно строит все такие векторы и для каждого из них идентифицирует соответствующую комбинацию элементов входных массивов.
function cartesianProduct(){
const N = arguments.length;
var arr_lengths = Array(N);
var digits = Array(N);
var num_tot = 1;
for(var i = 0; i < N; ++i){
const len = arguments[i].length;
if(!len){
num_tot = 0;
break;
}
digits[i] = 0;
num_tot *= (arr_lengths[i] = len);
}
var ret = Array(num_tot);
for(var num = 0; num < num_tot; ++num){
var item = Array(N);
for(var j = 0; j < N; ++j){ item[j] = arguments[j][digits[j]]; }
ret[num] = item;
for(var idx = 0; idx < N; ++idx){
if(digits[idx] == arr_lengths[idx]-1){
digits[idx] = 0;
}else{
digits[idx] += 1;
break;
}
}
}
return ret;
}
//------------------------------------------------------------------------------
let _f = (a, b) => [].concat(...a.map(a => b.map(b => [].concat(a, b))));
let cartesianProduct_rsp = (a, b, ...c) => b ? cartesianProduct_rsp(_f(a, b), ...c) : a;
//------------------------------------------------------------------------------
function cartesianProduct_sebnukem(a) {
var i, j, l, m, a1, o = [];
if (!a || a.length == 0) return a;
a1 = a.splice(0, 1)[0];
a = cartesianProduct_sebnukem(a);
for (i = 0, l = a1.length; i < l; i++) {
if (a && a.length) for (j = 0, m = a.length; j < m; j++)
o.push([a1[i]].concat(a[j]));
else
o.push([a1[i]]);
}
return o;
}
//------------------------------------------------------------------------------
const L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const args = [L, L, L, L, L, L];
let fns = {
'cartesianProduct': function(args){ return cartesianProduct(...args); },
'cartesianProduct_rsp': function(args){ return cartesianProduct_rsp(...args); },
'cartesianProduct_sebnukem': function(args){ return cartesianProduct_sebnukem(args); }
};
Object.keys(fns).forEach(fname => {
console.time(fname);
const ret = fns[fname](args);
console.timeEnd(fname);
});
с node v6.12.2
Я получаю следующие сроки:
cartesianProduct: 427.378ms
cartesianProduct_rsp: 1710.829ms
cartesianProduct_sebnukem: 593.351ms
Вы могли бы reduce
2D массив. использование flatMap
на массив аккумулятора, чтобы получить acc.length x curr.length
количество комбинаций в каждом цикле. [].concat(c, n)
используется потому что c
число в первой итерации и массив после.
const data = [ [1, 2], [10, 20], [100, 200, 300] ];
const output = data.reduce((acc, curr, i) =>
acc.flatMap(c => curr.map(n => [].concat(c, n)))
)
console.log(JSON.stringify(output))
(Это основано на ответе Нины Шольц)
По духу похож на другие, но очень читабелен.
function productOfTwo(a, b) {
return a.flatMap(c => b.map(d => [c, d].flat()));
}
[['a', 'b', 'c'], ['+', '-'], [1, 2, 3]].reduce(productOfTwo);
Более читаемая реализация
function productOfTwo(one, two) {
return one.flatMap(x => two.map(y => [].concat(x, y)));
}
function product(head = [], ...tail) {
if (tail.length === 0) return head;
return productOfTwo(head, product(...tail));
}
const test = product(
[1, 2, 3],
['a', 'b']
);
console.log(JSON.stringify(test));
Некоторые из ответов по этой теме терпят неудачу, когда любой из входных массивов содержит элемент массива. Тебе лучше проверить это.
В любом случае нет необходимости подчеркивать, lodash вообще. Я считаю, что это следует делать с чистым JS ES6, настолько функциональным, насколько это возможно.
Этот фрагмент кода использует карту сокращения и вложенную карту, просто чтобы получить декартово произведение двух массивов, однако второй массив получается из рекурсивного вызова той же функции с одним меньшим массивом; поэтому.. a[0].cartesian(...a.slice(1))
Array.prototype.cartesian = function(...a){
return a.length ? this.reduce((p,c) => (p.push(...a[0].cartesian(...a.slice(1)).map(e => a.length > 1 ? [c,...e] : [c,e])),p),[])
: this;
};
var arr = ['a', 'b', 'c'],
brr = [1,2,3],
crr = [[9],[8],[7]];
console.log(JSON.stringify(arr.cartesian(brr,crr)));
Библиотеки не нужны!:)
Требуются функции стрелок, хотя и, вероятно, не так эффективно.:/
const flatten = (xs) =>
xs.flat(Infinity)
const binaryCartesianProduct = (xs, ys) =>
xs.map((xi) => ys.map((yi) => [xi, yi])).flat()
const cartesianProduct = (...xss) =>
xss.reduce(binaryCartesianProduct, [[]]).map(flatten)
console.log(cartesianProduct([1,2,3], [1,2,3], [1,2,3]))
Просто для выбора очень простая реализация с использованием массива reduce
:
const array1 = ["day", "month", "year", "time"];
const array2 = ["from", "to"];
const process = (one, two) => [one, two].join(" ");
const product = array1.reduce((result, one) => result.concat(array2.map(two => process(one, two))), []);
Для тех, кто доволен решением ramda:
import { xprod, flatten } from 'ramda';
const cartessian = (...xs) => xs.reduce(xprod).map(flatten)
Или то же самое без зависимостей и двух блоков лего бесплатно (
xprod
а также
flatten
):
const flatten = xs => xs.flat();
const xprod = (xs, ys) => xs.flatMap(x => ys.map(y => [x, y]));
const cartessian = (...xs) => xs.reduce(xprod).map(flatten);
f=(a,b,c)=>a.flatMap(ai=>b.flatMap(bi=>c.map(ci=>[ai,bi,ci])))
Это для 3-х массивов.
Некоторые ответы давали возможность использовать любое количество массивов.
Это может легко сжиматься или расширяться до меньшего или большего количества массивов.
Мне нужны были комбинации одного подхода с повторениями, поэтому я мог использовать:
f(a,a,a)
но использовали:
f=(a,b,c)=>a.flatMap(a1=>a.flatMap(a2=>a.map(a3=>[a1,a2,a3])))
Простая, модифицированная версия кода @viebel в простом Javascript:
function cartesianProduct(...arrays) {
return arrays.reduce((a, b) => {
return [].concat(...a.map(x => {
const next = Array.isArray(x) ? x : [x];
return [].concat(b.map(y => next.concat(...[y])));
}));
});
}
const product = cartesianProduct([1, 2], [10, 20], [100, 200, 300]);
console.log(product);
/*
[ [ 1, 10, 100 ],
[ 1, 10, 200 ],
[ 1, 10, 300 ],
[ 1, 20, 100 ],
[ 1, 20, 200 ],
[ 1, 20, 300 ],
[ 2, 10, 100 ],
[ 2, 10, 200 ],
[ 2, 10, 300 ],
[ 2, 20, 100 ],
[ 2, 20, 200 ],
[ 2, 20, 300 ] ];
*/
Еще одна реализация. Не самый короткий или причудливый, но быстрый:
function cartesianProduct() {
var arr = [].slice.call(arguments),
intLength = arr.length,
arrHelper = [1],
arrToReturn = [];
for (var i = arr.length - 1; i >= 0; i--) {
arrHelper.unshift(arrHelper[0] * arr[i].length);
}
for (var i = 0, l = arrHelper[0]; i < l; i++) {
arrToReturn.push([]);
for (var j = 0; j < intLength; j++) {
arrToReturn[i].push(arr[j][(i / arrHelper[j + 1] | 0) % arr[j].length]);
}
}
return arrToReturn;
}
Нерекурсивный подход, который добавляет возможность фильтровать и изменять продукты перед их фактическим добавлением в набор результатов. Обратите внимание на использование.map, а не.forEach. В некоторых браузерах.map работает быстрее.
function crossproduct(arrays,rowtest,rowaction) {
// Calculate the number of elements needed in the result
var result_elems = 1, row_size = arrays.length;
arrays.map(function(array) {
result_elems *= array.length;
});
var temp = new Array(result_elems), result = [];
// Go through each array and add the appropriate element to each element of the temp
var scale_factor = result_elems;
arrays.map(function(array)
{
var set_elems = array.length;
scale_factor /= set_elems;
for(var i=result_elems-1;i>=0;i--) {
temp[i] = (temp[i] ? temp[i] : []);
var pos = i / scale_factor % set_elems;
// deal with floating point results for indexes, this took a little experimenting
if(pos < 1 || pos % 1 <= .5) {
pos = Math.floor(pos);
} else {
pos = Math.min(array.length-1,Math.ceil(pos));
}
temp[i].push(array[pos]);
if(temp[i].length===row_size) {
var pass = (rowtest ? rowtest(temp[i]) : true);
if(pass) {
if(rowaction) {
result.push(rowaction(temp[i]));
} else {
result.push(temp[i]);
}
}
}
}
});
return result;
}
Простое "разумное и визуально дружественное" решение.
// t = [i, length]
const moveThreadForwardAt = (t, tCursor) => {
if (tCursor < 0)
return true; // reached end of first array
const newIndex = (t[tCursor][0] + 1) % t[tCursor][1];
t[tCursor][0] = newIndex;
if (newIndex == 0)
return moveThreadForwardAt(t, tCursor - 1);
return false;
}
const cartesianMult = (...args) => {
let result = [];
const t = Array.from(Array(args.length)).map((x, i) => [0, args[i].length]);
let reachedEndOfFirstArray = false;
while (false == reachedEndOfFirstArray) {
result.push(t.map((v, i) => args[i][v[0]]));
reachedEndOfFirstArray = moveThreadForwardAt(t, args.length - 1);
}
return result;
}
// cartesianMult(
// ['a1', 'b1', 'c1'],
// ['a2', 'b2'],
// ['a3', 'b3', 'c3'],
// ['a4', 'b4']
// );
console.log(cartesianMult(
['a1'],
['a2', 'b2'],
['a3', 'b3']
));