How would you implement the Cartesian product of multiple arrays in JavaScript?
如何实现JavaScript中多个数组的笛卡尔积?
As an example,
作为一个例子,
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]...]
19 个解决方案
#1
80
Here is a functional solution to the problem (without any mutable variable!) using reduce
and flatten
, provided by underscore.js
:
下面是一个使用reduce和flatten的函数式解决方案(没有任何可变变量!)
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"]]
Remark: This solution was inspired by http://cwestblog.com/2011/05/02/cartesian-product-of-multiple-arrays/
注:这个解决方案的灵感来源于http://cwestblog.com/2011/05/02/cartesian- productof multi -arrays/
#2
43
2017 Update: 2-line answer with vanilla JS
All of the answers here are overly complicated, most of them take 20 lines of code or even more.
这里所有的答案都过于复杂,大多数都需要20行甚至更多的代码。
This example uses just two lines of vanilla JavaScript, no lodash, underscore or other libraries:
这个示例只使用了两行普通的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;
Update:
This is the same as above but improved to strictly follow the Airbnb JavaScript Style Guide - validated using ESLint with eslint-config-airbnb-base:
这与上面的内容相同,但经过改进,严格遵循了Airbnb的JavaScript样式指南——使用ESLint与eslin -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);
Special thanks to ZuBB for letting me know about linter problems with the original code.
特别感谢ZuBB让我了解了原始代码的linter问题。
Example
This is the exact example from your question:
这就是你问题中的一个例子:
let output = cartesian([1,2],[10,20],[100,200,300]);
Output
This is the output of that command:
这是该命令的输出:
[ [ 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 ] ]
Demo
See demos on:
看到演示:
- JS Bin with Babel (for old browsers)
- 带有Babel的JS Bin(用于旧浏览器)
- JS Bin without Babel (for modern browsers)
- 没有Babel的JS Bin(用于现代浏览器)
Syntax
The syntax that I used here is nothing new. My example uses the spread operator and the rest parameters - features of JavaScript defined in the 6th edition of the ECMA-262 standard published on June 2015 and developed much earlier, better known as ES6 or ES2015. See:
我在这里使用的语法并不新鲜。我的示例使用了扩展操作符和rest参数——在2015年6月发布的ECMA-262标准的第6版中定义的JavaScript特性,并开发了更早的版本,即ES6或ES2015。看到的:
- http://www.ecma-international.org/ecma-262/6.0/
- 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/Functions/rest_parameters
- https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Spread_operator
- https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Spread_operator
It makes code like this so simple that it's a sin not to use it. For old platforms that don't support it natively you can always use Babel or other tools to transpile it to older syntax - and in fact my example transpiled by Babel is still shorter and simpler than most of the examples here, but it doesn't really matter because the output of transpilation is not something that you need to understand or maintain, it's just a fact that I found interesting.
它使这样的代码如此简单以至于不使用它是一种罪过。老平台不支持本地你可以总是使用巴别塔或其他工具来transpile老语法——事实上我例如transpiled巴贝尔仍然较短,比最简单的例子,但这并不重要,因为输出transpilation不是你需要理解和维护,它只是一个事实,我觉得很有趣。
Conclusion
There's no need to write hundred of lines of code that is hard to maintain and there is no need to use entire libraries for such a simple thing, when two lines of vanilla JavaScript can easily get the job done. As you can see it really pays off to use modern features of the language and in cases where you need to support archaic platforms with no native support of the modern features you can always use Babel or other tools to transpile the new syntax to the old one.
不需要编写几百行难以维护的代码,也不需要为这么简单的事情使用整个库,只要两行普通的JavaScript就可以轻松完成任务。正如您所看到的,使用语言的现代特性是值得的,并且在您需要支持过时的平台而不支持现代特性的情况下,您总是可以使用Babel或其他工具将新的语法转换为旧的语法。
Don't code like it's 1995
JavaScript evolves and it does so for a reason. TC39 does an amazing job of the language design with adding new features and the browser vendors do an amazing job of implementing those features.
JavaScript的发展是有原因的。TC39在语言设计方面做得非常出色,添加了新特性,浏览器供应商在实现这些特性方面做得非常出色。
To see the current state of native support of any given feature in the browsers, see:
要查看浏览器中任何给定特性的本地支持的当前状态,请参见:
- http://caniuse.com/
- http://caniuse.com/
- https://kangax.github.io/compat-table/
- https://kangax.github.io/compat-table/
To see the support in Node versions, see:
要查看节点版本中的支持,请参见:
- http://node.green/
- http://node.green/
To use modern syntax on platforms that don't support it natively, use Babel:
要在不支持现代语法的平台上使用现代语法,请使用Babel:
- https://babeljs.io/
- https://babeljs.io/
#3
30
Here's a modified version of @viebel's code in plain Javascript, without using any library:
下面是一个修改后的@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);
#4
27
it seems the community thinks this to be trivial and or easy to find a reference implementation, upon brief inspection i couldn't or maybe it's just that i like re-inventing the wheel or solving classroom-like programming problems either way its your lucky day:
似乎社区认为这很琐碎,或者很容易找到一个参考实现,经过简短的检查,我不能或者也许只是我喜欢重新发明*或者解决类似于课堂的编程问题不管怎样,这都是你的幸运日:
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]
]
full reference implementation that's relatively efficient... :-D
相对高效的完整参考实现……:- d
on efficiency: you could gain some by taking the if out of the loop and having 2 separate loops since it is technically constant and you'd be helping with branch prediction and all that mess, but that point is kind of moot in javascript
关于效率:你可以通过将if从循环中取出来获得一些,并且有两个单独的循环,因为它在技术上是常量,并且你会帮助进行分支预测和所有的混乱,但是这一点在javascript中是不可能的。
anywho, enjoy -ck
anywho,享受ck
#5
14
Here's a non-fancy, straightforward recursive solution:
这里有一个简单的递归解决方案:
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]]
#6
9
Using a typical backtracking with ES6 generators,
使用典型的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%; }
Below there is a similar version compatible with older browsers.
下面是与旧浏览器兼容的类似版本。
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%; }
#7
8
Here is a recursive way that uses an ECMAScript 2015 generator function so you don't have to create all of the tuples at once:
下面是一种使用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]))));
#8
7
The following efficient generator function returns the cartesian product of all given iterables:
以下高效的生成器函数将返回所有给定的iterables的cartesian产品:
// Return cartesian product of given iterables:
function* cartesian(head, ...tail) {
const remaining = tail.length > 0 ? cartesian(...tail) : [[]];
for (let r of remaining) for (let h of head) yield [h, ...r];
}
// Example:
console.log(...cartesian([1, 2], [10, 20], [100, 200, 300]));
It accepts arrays, strings, sets and all other objects implementing the iterable protocol.
它接受实现可迭代协议的数组、字符串、集合和所有其他对象。
Following the specification of the n-ary cartesian product it yields
它按照n元笛卡尔积的规范生成
-
[]
if one or more given iterables are empty, e.g.[]
or''
- 如果一个或多个给定的迭代是空的,例如[]或"
-
[[a]]
if a single iterable containing a single valuea
is given. - 如果给定一个包含单个值a的迭代器。
All other cases are handled as expected as demonstrated by the following test cases:
所有其他情况都按照预期的那样处理,如下面的测试用例所示:
// Return cartesian product of given iterables:
function* cartesian(head, ...tail) {
const remaining = tail.length > 0 ? cartesian(...tail) : [[]];
for (let r of remaining) 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]]
#9
6
A coffeescript version with lodash:
一个带有lodash的coffeescript版本:
_ = require("lodash")
cartesianProduct = ->
return _.reduceRight(arguments, (a,b) ->
_.flatten(_.map(a,(x) -> _.map b, (y) -> x.concat(y)), true)
, [ [] ])
#10
6
This is a pure ES6 solution using arrow functions
这是一个使用箭头函数的纯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)));
#11
3
A few of the answers under this topic fail when any of the input arrays contains an array item. You you better check that.
当任何一个输入数组包含一个数组项时,这个主题下的一些答案就会失败。你最好检查一下。
Anyways no need for underscore, lodash whatsoever. I believe this one should do it with pure JS ES6, as functional as it gets.
无论如何不需要下划线,也不需要破折号。我认为这个应该用纯JS ES6来实现,尽可能地发挥功能。
This piece of code uses a reduce and a nested map, simply to get the cartesian product of two arrays however the second array comes from a recursive call to the same function with one less array; hence.. a[0].cartesian(...a.slice(1))
这段代码使用了一个reduce和一个嵌套映射,仅仅是为了得到两个数组的笛卡尔积但是第二个数组来自于对同一个函数的递归调用,它的数组更少;因此. .一个[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)));
#12
1
A non-recursive approach that adds the ability to filter and modify the products before actually adding them to the result set. Note the use of .map rather than .forEach. In some browsers, .map runs faster.
一种非递归方法,在将产品添加到结果集之前添加过滤和修改功能。注意使用.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;
}
#13
1
In my particular setting, the "old-fashioned" approach seemed to be more efficient than the methods based on more modern features. Below is the code (including a small comparison with other solutions posted in this thread by @rsp and @sebnukem) should it prove useful to someone else as well.
在我的特定环境中,“老式”方法似乎比基于更现代特性的方法更有效。下面是代码(包括@rsp和@sebnukem在这个线程中发布的其他解决方案的一个小比较),如果它对其他人也有用的话。
The idea is following. Let's say we are constructing the outer product of N
arrays, a_1,...,a_N
each of which has m_i
components. The outer product of these arrays has M=m_1*m_2*...*m_N
elements and we can identify each of them with a N-
dimensional vector the components of which are positive integers and i
-th component is strictly bounded from above by m_i
. For example, the vector (0, 0, ..., 0)
would correspond to the particular combination within which one takes the first element from each array, while (m_1-1, m_2-1, ..., m_N-1)
is identified with the combination where one takes the last element from each array. Thus in order to construct all M
combinations, the function below consecutively constructs all such vectors and for each of them identifies the corresponding combination of the elements of the input arrays.
我们的想法是。假设我们正在构造N个数组的外积,a_1。每个a_N都有m_i分量。这些数组的外积有M=m_1*m_2*…*m_N元素,我们可以用一个n维向量来识别它们它们的分量都是正整数,第i个分量从上面被m_i严格限定。例如,向量(0,0,…)(m_1-1, m_2-1,…)将对应于从每个数组中获取第一个元素的特定组合,而(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);
});
with node v6.12.2
, I get following timings:
对于节点v6.12.2,我得到以下时间安排:
cartesianProduct: 427.378ms
cartesianProduct_rsp: 1710.829ms
cartesianProduct_sebnukem: 593.351ms
#14
1
Just for a choice a real simple implementation using array's reduce
:
只需选择一个使用数组的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))), []);
#15
0
I noticed that nobody posted a solution that allows a function to be passed to process each combination, so here is my solution:
我注意到没有人发布一个允许传递一个函数来处理每个组合的解决方案,所以我的解决方案是:
const _ = require('lodash')
function combinations(arr, f, xArr = []) {
return arr.length>1
? _.flatMap(arr[0], x => combinations(arr.slice(1), f, xArr.concat(x)))
: arr[0].map(x => f(...xArr.concat(x)))
}
// use case
const greetings = ["Hello", "Goodbye"]
const places = ["World", "Planet"]
const punctuationMarks = ["!", "?"]
combinations([greetings,places,punctuationMarks], (greeting, place, punctuationMark) => `${greeting} ${place}${punctuationMark}`)
.forEach(row => console.log(row))
Output:
输出:
Hello World!
Hello World?
Hello Planet!
Hello Planet?
Goodbye World!
Goodbye World?
Goodbye Planet!
Goodbye Planet?
#16
0
Plain JS brute force approach that takes an array of arrays as input.
普通的JS蛮力方法,以数组数组作为输入。
var cartesian = function(arrays) {
var product = [];
var precals = [];
var length = arrays.reduce(function(acc, curr) {
return acc * curr.length
}, 1);
for (var i = 0; i < arrays.length; i++) {
var array = arrays[i];
var mod = array.length;
var div = i > 0 ? precals[i - 1].div * precals[i - 1].mod : 1;
precals.push({
div: div,
mod: mod
});
}
for (var j = 0; j < length; j++) {
var item = [];
for (var i = 0; i < arrays.length; i++) {
var array = arrays[i];
var precal = precals[i];
var k = (~~(j / precal.div)) % precal.mod;
item.push(array[k]);
}
product.push(item);
}
return product;
};
cartesian([
[1],
[2, 3]
]);
cartesian([
[1],
[2, 3],
[4, 5, 6]
]);
#17
0
A simple "mind and visually friendly" solution.
一个简单的“头脑和视觉上友好的”解决方案。
// 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']
));
#18
0
var chars = ['A', 'B', 'C']
var nums = [1, 2, 3]
var cartesianProduct = function() {
return _.reduce(arguments, function(a, b) {
return _.flatten(_.map(a, function(x) {
return _.map(b, function(y) {
return x.concat(y);
});
}), true);
}, [
[]
]);
};
console.log(cartesianProduct(chars, nums))
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
Just converted @dummersl's answer from CoffeScript to JavaScript. It just works.
将@dummersl的答案从CoffeScript转换为JavaScript。它只是工作。
var chars = ['A', 'B', 'C']
var nums = [1, 2, 3]
var cartesianProduct = function() {
return _.reduce(arguments, function(a, b) {
return _.flatten(_.map(a, function(x) {
return _.map(b, function(y) {
return x.concat(y);
});
}), true);
}, [[]]);
};
console.log( cartesianProduct(chars, nums) )
#19
0
A single line approach, for better reading with indentations.
单行方法,用于更好地读取缩进。
result = data.reduce(
(a, b) => a.reduce(
(r, v) => r.concat(b.map(w => [].concat(v, w))),
[]
)
);
It takes a single array with arrays of wanted cartesian items.
它使用一个带有想要的笛卡尔项数组的数组。
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; }
#1
80
Here is a functional solution to the problem (without any mutable variable!) using reduce
and flatten
, provided by underscore.js
:
下面是一个使用reduce和flatten的函数式解决方案(没有任何可变变量!)
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"]]
Remark: This solution was inspired by http://cwestblog.com/2011/05/02/cartesian-product-of-multiple-arrays/
注:这个解决方案的灵感来源于http://cwestblog.com/2011/05/02/cartesian- productof multi -arrays/
#2
43
2017 Update: 2-line answer with vanilla JS
All of the answers here are overly complicated, most of them take 20 lines of code or even more.
这里所有的答案都过于复杂,大多数都需要20行甚至更多的代码。
This example uses just two lines of vanilla JavaScript, no lodash, underscore or other libraries:
这个示例只使用了两行普通的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;
Update:
This is the same as above but improved to strictly follow the Airbnb JavaScript Style Guide - validated using ESLint with eslint-config-airbnb-base:
这与上面的内容相同,但经过改进,严格遵循了Airbnb的JavaScript样式指南——使用ESLint与eslin -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);
Special thanks to ZuBB for letting me know about linter problems with the original code.
特别感谢ZuBB让我了解了原始代码的linter问题。
Example
This is the exact example from your question:
这就是你问题中的一个例子:
let output = cartesian([1,2],[10,20],[100,200,300]);
Output
This is the output of that command:
这是该命令的输出:
[ [ 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 ] ]
Demo
See demos on:
看到演示:
- JS Bin with Babel (for old browsers)
- 带有Babel的JS Bin(用于旧浏览器)
- JS Bin without Babel (for modern browsers)
- 没有Babel的JS Bin(用于现代浏览器)
Syntax
The syntax that I used here is nothing new. My example uses the spread operator and the rest parameters - features of JavaScript defined in the 6th edition of the ECMA-262 standard published on June 2015 and developed much earlier, better known as ES6 or ES2015. See:
我在这里使用的语法并不新鲜。我的示例使用了扩展操作符和rest参数——在2015年6月发布的ECMA-262标准的第6版中定义的JavaScript特性,并开发了更早的版本,即ES6或ES2015。看到的:
- http://www.ecma-international.org/ecma-262/6.0/
- 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/Functions/rest_parameters
- https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Spread_operator
- https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Spread_operator
It makes code like this so simple that it's a sin not to use it. For old platforms that don't support it natively you can always use Babel or other tools to transpile it to older syntax - and in fact my example transpiled by Babel is still shorter and simpler than most of the examples here, but it doesn't really matter because the output of transpilation is not something that you need to understand or maintain, it's just a fact that I found interesting.
它使这样的代码如此简单以至于不使用它是一种罪过。老平台不支持本地你可以总是使用巴别塔或其他工具来transpile老语法——事实上我例如transpiled巴贝尔仍然较短,比最简单的例子,但这并不重要,因为输出transpilation不是你需要理解和维护,它只是一个事实,我觉得很有趣。
Conclusion
There's no need to write hundred of lines of code that is hard to maintain and there is no need to use entire libraries for such a simple thing, when two lines of vanilla JavaScript can easily get the job done. As you can see it really pays off to use modern features of the language and in cases where you need to support archaic platforms with no native support of the modern features you can always use Babel or other tools to transpile the new syntax to the old one.
不需要编写几百行难以维护的代码,也不需要为这么简单的事情使用整个库,只要两行普通的JavaScript就可以轻松完成任务。正如您所看到的,使用语言的现代特性是值得的,并且在您需要支持过时的平台而不支持现代特性的情况下,您总是可以使用Babel或其他工具将新的语法转换为旧的语法。
Don't code like it's 1995
JavaScript evolves and it does so for a reason. TC39 does an amazing job of the language design with adding new features and the browser vendors do an amazing job of implementing those features.
JavaScript的发展是有原因的。TC39在语言设计方面做得非常出色,添加了新特性,浏览器供应商在实现这些特性方面做得非常出色。
To see the current state of native support of any given feature in the browsers, see:
要查看浏览器中任何给定特性的本地支持的当前状态,请参见:
- http://caniuse.com/
- http://caniuse.com/
- https://kangax.github.io/compat-table/
- https://kangax.github.io/compat-table/
To see the support in Node versions, see:
要查看节点版本中的支持,请参见:
- http://node.green/
- http://node.green/
To use modern syntax on platforms that don't support it natively, use Babel:
要在不支持现代语法的平台上使用现代语法,请使用Babel:
- https://babeljs.io/
- https://babeljs.io/
#3
30
Here's a modified version of @viebel's code in plain Javascript, without using any library:
下面是一个修改后的@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);
#4
27
it seems the community thinks this to be trivial and or easy to find a reference implementation, upon brief inspection i couldn't or maybe it's just that i like re-inventing the wheel or solving classroom-like programming problems either way its your lucky day:
似乎社区认为这很琐碎,或者很容易找到一个参考实现,经过简短的检查,我不能或者也许只是我喜欢重新发明*或者解决类似于课堂的编程问题不管怎样,这都是你的幸运日:
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]
]
full reference implementation that's relatively efficient... :-D
相对高效的完整参考实现……:- d
on efficiency: you could gain some by taking the if out of the loop and having 2 separate loops since it is technically constant and you'd be helping with branch prediction and all that mess, but that point is kind of moot in javascript
关于效率:你可以通过将if从循环中取出来获得一些,并且有两个单独的循环,因为它在技术上是常量,并且你会帮助进行分支预测和所有的混乱,但是这一点在javascript中是不可能的。
anywho, enjoy -ck
anywho,享受ck
#5
14
Here's a non-fancy, straightforward recursive solution:
这里有一个简单的递归解决方案:
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]]
#6
9
Using a typical backtracking with ES6 generators,
使用典型的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%; }
Below there is a similar version compatible with older browsers.
下面是与旧浏览器兼容的类似版本。
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%; }
#7
8
Here is a recursive way that uses an ECMAScript 2015 generator function so you don't have to create all of the tuples at once:
下面是一种使用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]))));
#8
7
The following efficient generator function returns the cartesian product of all given iterables:
以下高效的生成器函数将返回所有给定的iterables的cartesian产品:
// Return cartesian product of given iterables:
function* cartesian(head, ...tail) {
const remaining = tail.length > 0 ? cartesian(...tail) : [[]];
for (let r of remaining) for (let h of head) yield [h, ...r];
}
// Example:
console.log(...cartesian([1, 2], [10, 20], [100, 200, 300]));
It accepts arrays, strings, sets and all other objects implementing the iterable protocol.
它接受实现可迭代协议的数组、字符串、集合和所有其他对象。
Following the specification of the n-ary cartesian product it yields
它按照n元笛卡尔积的规范生成
-
[]
if one or more given iterables are empty, e.g.[]
or''
- 如果一个或多个给定的迭代是空的,例如[]或"
-
[[a]]
if a single iterable containing a single valuea
is given. - 如果给定一个包含单个值a的迭代器。
All other cases are handled as expected as demonstrated by the following test cases:
所有其他情况都按照预期的那样处理,如下面的测试用例所示:
// Return cartesian product of given iterables:
function* cartesian(head, ...tail) {
const remaining = tail.length > 0 ? cartesian(...tail) : [[]];
for (let r of remaining) 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]]
#9
6
A coffeescript version with lodash:
一个带有lodash的coffeescript版本:
_ = require("lodash")
cartesianProduct = ->
return _.reduceRight(arguments, (a,b) ->
_.flatten(_.map(a,(x) -> _.map b, (y) -> x.concat(y)), true)
, [ [] ])
#10
6
This is a pure ES6 solution using arrow functions
这是一个使用箭头函数的纯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)));
#11
3
A few of the answers under this topic fail when any of the input arrays contains an array item. You you better check that.
当任何一个输入数组包含一个数组项时,这个主题下的一些答案就会失败。你最好检查一下。
Anyways no need for underscore, lodash whatsoever. I believe this one should do it with pure JS ES6, as functional as it gets.
无论如何不需要下划线,也不需要破折号。我认为这个应该用纯JS ES6来实现,尽可能地发挥功能。
This piece of code uses a reduce and a nested map, simply to get the cartesian product of two arrays however the second array comes from a recursive call to the same function with one less array; hence.. a[0].cartesian(...a.slice(1))
这段代码使用了一个reduce和一个嵌套映射,仅仅是为了得到两个数组的笛卡尔积但是第二个数组来自于对同一个函数的递归调用,它的数组更少;因此. .一个[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)));
#12
1
A non-recursive approach that adds the ability to filter and modify the products before actually adding them to the result set. Note the use of .map rather than .forEach. In some browsers, .map runs faster.
一种非递归方法,在将产品添加到结果集之前添加过滤和修改功能。注意使用.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;
}
#13
1
In my particular setting, the "old-fashioned" approach seemed to be more efficient than the methods based on more modern features. Below is the code (including a small comparison with other solutions posted in this thread by @rsp and @sebnukem) should it prove useful to someone else as well.
在我的特定环境中,“老式”方法似乎比基于更现代特性的方法更有效。下面是代码(包括@rsp和@sebnukem在这个线程中发布的其他解决方案的一个小比较),如果它对其他人也有用的话。
The idea is following. Let's say we are constructing the outer product of N
arrays, a_1,...,a_N
each of which has m_i
components. The outer product of these arrays has M=m_1*m_2*...*m_N
elements and we can identify each of them with a N-
dimensional vector the components of which are positive integers and i
-th component is strictly bounded from above by m_i
. For example, the vector (0, 0, ..., 0)
would correspond to the particular combination within which one takes the first element from each array, while (m_1-1, m_2-1, ..., m_N-1)
is identified with the combination where one takes the last element from each array. Thus in order to construct all M
combinations, the function below consecutively constructs all such vectors and for each of them identifies the corresponding combination of the elements of the input arrays.
我们的想法是。假设我们正在构造N个数组的外积,a_1。每个a_N都有m_i分量。这些数组的外积有M=m_1*m_2*…*m_N元素,我们可以用一个n维向量来识别它们它们的分量都是正整数,第i个分量从上面被m_i严格限定。例如,向量(0,0,…)(m_1-1, m_2-1,…)将对应于从每个数组中获取第一个元素的特定组合,而(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);
});
with node v6.12.2
, I get following timings:
对于节点v6.12.2,我得到以下时间安排:
cartesianProduct: 427.378ms
cartesianProduct_rsp: 1710.829ms
cartesianProduct_sebnukem: 593.351ms
#14
1
Just for a choice a real simple implementation using array's reduce
:
只需选择一个使用数组的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))), []);
#15
0
I noticed that nobody posted a solution that allows a function to be passed to process each combination, so here is my solution:
我注意到没有人发布一个允许传递一个函数来处理每个组合的解决方案,所以我的解决方案是:
const _ = require('lodash')
function combinations(arr, f, xArr = []) {
return arr.length>1
? _.flatMap(arr[0], x => combinations(arr.slice(1), f, xArr.concat(x)))
: arr[0].map(x => f(...xArr.concat(x)))
}
// use case
const greetings = ["Hello", "Goodbye"]
const places = ["World", "Planet"]
const punctuationMarks = ["!", "?"]
combinations([greetings,places,punctuationMarks], (greeting, place, punctuationMark) => `${greeting} ${place}${punctuationMark}`)
.forEach(row => console.log(row))
Output:
输出:
Hello World!
Hello World?
Hello Planet!
Hello Planet?
Goodbye World!
Goodbye World?
Goodbye Planet!
Goodbye Planet?
#16
0
Plain JS brute force approach that takes an array of arrays as input.
普通的JS蛮力方法,以数组数组作为输入。
var cartesian = function(arrays) {
var product = [];
var precals = [];
var length = arrays.reduce(function(acc, curr) {
return acc * curr.length
}, 1);
for (var i = 0; i < arrays.length; i++) {
var array = arrays[i];
var mod = array.length;
var div = i > 0 ? precals[i - 1].div * precals[i - 1].mod : 1;
precals.push({
div: div,
mod: mod
});
}
for (var j = 0; j < length; j++) {
var item = [];
for (var i = 0; i < arrays.length; i++) {
var array = arrays[i];
var precal = precals[i];
var k = (~~(j / precal.div)) % precal.mod;
item.push(array[k]);
}
product.push(item);
}
return product;
};
cartesian([
[1],
[2, 3]
]);
cartesian([
[1],
[2, 3],
[4, 5, 6]
]);
#17
0
A simple "mind and visually friendly" solution.
一个简单的“头脑和视觉上友好的”解决方案。
// 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']
));
#18
0
var chars = ['A', 'B', 'C']
var nums = [1, 2, 3]
var cartesianProduct = function() {
return _.reduce(arguments, function(a, b) {
return _.flatten(_.map(a, function(x) {
return _.map(b, function(y) {
return x.concat(y);
});
}), true);
}, [
[]
]);
};
console.log(cartesianProduct(chars, nums))
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
Just converted @dummersl's answer from CoffeScript to JavaScript. It just works.
将@dummersl的答案从CoffeScript转换为JavaScript。它只是工作。
var chars = ['A', 'B', 'C']
var nums = [1, 2, 3]
var cartesianProduct = function() {
return _.reduce(arguments, function(a, b) {
return _.flatten(_.map(a, function(x) {
return _.map(b, function(y) {
return x.concat(y);
});
}), true);
}, [[]]);
};
console.log( cartesianProduct(chars, nums) )
#19
0
A single line approach, for better reading with indentations.
单行方法,用于更好地读取缩进。
result = data.reduce(
(a, b) => a.reduce(
(r, v) => r.concat(b.map(w => [].concat(v, w))),
[]
)
);
It takes a single array with arrays of wanted cartesian items.
它使用一个带有想要的笛卡尔项数组的数组。
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; }