JavaScript中多个数组的笛卡尔积

时间:2012-09-06 15:59:57

标签: javascript algorithm functional-programming

如何在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]...]

35 个答案:

答案 0 :(得分:86)

以下是使用reduce提供的flattenunderscore.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);
    }, [ [] ]);
}

// [[1,3,"a"],[1,3,"b"],[1,4,"a"],[1,4,"b"],[2,3,"a"],[2,3,"b"],[2,4,"a"],[2,4,"b"]]
console.log(cartesianProductOf([1, 2], [3, 4], ['a']));  
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore.js"></script>

备注:此解决方案的灵感来自http://cwestblog.com/2011/05/02/cartesian-product-of-multiple-arrays/

答案 1 :(得分:73)

2017年更新:与香草JS的2行答案

这里的所有答案都是过于复杂,其中大多数都需要20行代码甚至更多。

此示例仅使用两行vanilla 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;

更新

这与上述相同,但经过改进后严格遵循Airbnb JavaScript Style Guide - 使用ESLint 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 让我了解原始代码的linter问题。

实施例

这是您问题的确切示例:

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 ] ]

演示

参见演示:

语法

我在这里使用的语法并不新鲜。 我的例子使用了扩展运算符和其余参数 - 在2015年6月发布的第6版ECMA-262标准中定义的JavaScript特性,并且更早开发,更好地称为ES6或ES2015。参见:

它使这样的代码如此简单,以至于不使用它是一种罪过。对于本身不支持它的旧平台,你总是可以使用Babel或其他工具将其转换为较旧的语法 - 事实上,我的Babel描述的例子仍然比这里的大多数例子更短更简单,但它没有&真的很重要,因为翻译的输出不是你需要理解或维护的东西,这只是我觉得有趣的事实。

结论

没有必要编写难以维护的数百行代码,并且当两行vanilla JavaScript可以轻松完成工作时,不需要将整个库用于这么简单的事情。正如您所看到的,使用该语言的现代功能确实是值得的,如果您需要支持没有现代功能原生支持的古老平台,您可以始终使用Babel或其他工具将新语法转换为旧语法

不要像1995年那样编码

JavaScript发展并且出于某种原因这样做了。 TC39通过添加新功能在语言设计方面做得非常出色,浏览器供应商在实现这些功能方面做得非常出色。

要查看浏览器中任何给定功能的当前支持状态,请参阅:

要查看Node版本中的支持,请参阅:

要在不支持本机的平台上使用现代语法,请使用Babel:

答案 2 :(得分:36)

以下是普通Javascript中@ viebel代码的修改版本,不使用任何库:

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);

答案 3 :(得分:27)

似乎社区认为这是微不足道的,或者很容易找到参考实现,经过简单的检查我不能或者可能只是因为我喜欢重新发明轮子或解决类似教室的编程问题你的幸运日:

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

anywho,enjoy -ck

答案 4 :(得分:19)

这是一个非常奇特,直截了当的递归解决方案:

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]]

答案 5 :(得分:19)

以下有效generator function会返回所有给定iterables的笛卡尔积:

&#13;
&#13;
// 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]));
&#13;
&#13;
&#13;

它接受实现iterable protocol的数组,字符串,集和所有其他对象。

遵循n-ary cartesian product的规范,产生

  • []如果一个或多个给定的iterables为空,例如[]''
  • [[a]]如果给出了包含单个值a的单个迭代。

所有其他案例均按预期处理,如以下测试案例所示:

&#13;
&#13;
// 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]]
&#13;
&#13;
&#13;

答案 6 :(得分:9)

使用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%; }

答案 7 :(得分:9)

这是一种使用ECMAScript 2015 generator function的递归方式,因此您不必一次创建所有元组:

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)

这是使用arrow functions

的纯ES6解决方案

&#13;
&#13;
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)));
&#13;
&#13;
&#13;

答案 9 :(得分:7)

这是使用本机ES2019 flatMap的单线。无需库,只需一个现代浏览器(或翻译器):

data.reduce((a, b) => a.flatMap(x => b.map(y => [...x, y])), [[]]);

从本质上讲,这是viebel回答的现代版本,没有lodash。

答案 10 :(得分:6)

带有lodash的coffeescript版本:

_ = require("lodash")
cartesianProduct = ->
    return _.reduceRight(arguments, (a,b) ->
        _.flatten(_.map(a,(x) -> _.map b, (y) -> x.concat(y)), true)
    , [ [] ])

答案 11 :(得分:4)

单行方法,以便更好地阅读缩进。

result = data.reduce(
    (a, b) => a.reduce(
        (r, v) => r.concat(b.map(w => [].concat(v, w))),
        []
    )
);

它需要一个包含所需笛卡儿项目数组的数组。

&#13;
&#13;
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(' ')));
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
&#13;
&#13;

答案 12 :(得分:3)

当任何输入数组包含数组项时,本主题下的一些答案将失败。你最好检查一下。

无论如何都不需要下划线,无论如何。我相信这个应该使用纯JS ES6来实现,因为它具有功能性。

这段代码使用reduce和嵌套映射,只是为了获得两个数组的笛卡尔积,然而第二个数组来自对同一函数的递归调用,只有一个数组;因此.. a[0].cartesian(...a.slice(1))

&#13;
&#13;
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))); 
&#13;
&#13;
&#13;

答案 13 :(得分:2)

您可以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) =>
  acc.flatMap(c => curr.map(n => [].concat(c, n)))
)

console.log(JSON.stringify(output))

(基于Nina Scholz's answer

答案 14 :(得分:2)

仅需几行即可使用现代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')
);

答案 15 :(得分:2)

这被标记为 functional-programming ,所以让我们看一下List monad

  

此单子列表的一个应用程序代表不确定性计算。 List 可以保存算法中所有执行路径的结果 ...

听起来像cartesian perfect 完美。 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]))

答案 16 :(得分:2)

在我的特定环境中,&#34;老式的&#34;方法似乎比基于更现代特征的方法更有效。下面是代码(包括与@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

答案 17 :(得分:1)

另一个更简化的 2021 风格答案,仅使用 reduce、map 和 concat 方法:

const cartesian = (...arr) => arr.reduce((a,c) => a.map(e => c.map(f => e.concat([f]))).reduce((a,c) => a.concat(c), []), [[]]);

console.log(cartesian([1, 2], [10, 20], [100, 200, 300]));

答案 18 :(得分:1)

这是一个仅使用 flatMapmap 的递归单行:

const inp = [
  [1, 2],
  [10, 20],
  [100, 200, 300]
];

const cartesian = (first, ...rest) => 
  rest.length ? first.flatMap(v => cartesian(...rest).map(c => [v].concat(c))) 
              : first;

console.log(cartesian(...inp));

答案 19 :(得分:1)

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])))

答案 20 :(得分:1)

对于需要TypeScript的用户(重新实现@Danny的答案)

clf = RandomForestClassifier(n_estimators=100, max_depth=2,random_state=0)
clf.fit(X, y)
#After the fit step
clf.feature_importances_ 

答案 21 :(得分:1)

只需选择使用数组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))), []);

答案 22 :(得分:1)

更具可读性的实现

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));

答案 23 :(得分:1)

一种非递归方法,可以在将产品实际添加到结果集之前添加过滤和修改产品的功能。注意使用.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;
    }

答案 24 :(得分:0)

2021 年版 David Tang 的伟大 answer
也受到 Neil Mountford 的 answer

的启发
  • 快(比接受的答案快 95%)
  • 适用于任何数据类型

const getAllCombinations = (arraysToCombine) => {
  const divisors = [];
  let combinationsCount = 1;
  for (let i = arraysToCombine.length - 1; i >= 0; i--) {
      divisors[i] = divisors[i + 1] ? divisors[i + 1] * arraysToCombine[i + 1].length : 1;
      combinationsCount *= (arraysToCombine[i].length || 1);
  }

  const getCombination = (n, arrays, divisors) => arrays.reduce((acc, arr, i) => {
      acc.push(arr[Math.floor(n / divisors[i]) % arr.length]);
      return acc;
  }, []);

  const combinations = [];
  for (let i = 0; i < combinationsCount; i++) {
      combinations.push(getCombination(i, arraysToCombine, divisors));
  }
  return combinations;
};

console.log(getAllCombinations([['a', 'b'], ['c', 'z'], ['d', 'e', 'f']]));

基准:https://jsbench.me/gdkmxhm36d/1

答案 25 :(得分:0)

对于那些对ramda解决方案感到满意的人:

import { xprod, flatten } from 'ramda';

const cartessian = (...xs) => xs.reduce(xprod).map(flatten)

还是没有依赖项和两个免费的乐高积木(xprodflatten)相同:

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);

答案 26 :(得分:0)

实现这一点的最简单方法是使用 ES6 方式。 所有人都必须了解维恩图的工作原理。所以, 同样,ES6 为我们提供了一种对任何类型的数据结构执行所有集合操作的方法。

让我们看看下面的例子

var arr1 = [1,2,2,3,5] var arr2 = [2,3,5,6,7]

arr1.filter(x => arr2.includes(x))

结果 => [2,2,3,5]

我们可以看到结果中重复了“2”,这可以通过使用任何一个数组上提供的 Set Class 来避免 如上述答案中所述

[... new Set(arr1)].filter(x => arr2.includes(x))

结果 => [2,3,5]

答案 27 :(得分:0)

记录

这是我的版本。我使用最简单的javascript迭代器“ for()”制作了它,因此它在每种情况下都兼容,并且具有最佳性能。

function cartesian(arrays){
    var quant = 1, counters = [], retArr = [];

    // Counts total possibilities and build the counters Array;
    for(var i=0;i<arrays.length;i++){
        counters[i] = 0;
        quant *= arrays[i].length;
    }

    // iterate all possibilities
    for(var i=0,nRow;i<quant;i++){
        nRow = [];
        for(var j=0;j<counters.length;j++){
            if(counters[j] < arrays[j].length){
                nRow.push(arrays[j][counters[j]]);
            } else { // in case there is no such an element it restarts the current counter
                counters[j] = 0;
                nRow.push(arrays[j][counters[j]]);
            }
            counters[j]++;
        }
        retArr.push(nRow);
    }
    return retArr;
}

最诚挚的问候。

答案 28 :(得分:0)

无需任何库! :)

虽然需要箭头功能,但可能没有那么高效。 :/

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]))

答案 29 :(得分:0)

使用纯Javascript的@viebel代码的简单修改版本:

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 ] ];
*/

答案 30 :(得分:0)

另一个实现。不是最短或最花哨的,而是最快的:

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;
}

答案 31 :(得分: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>

刚刚将CoffeScript的@dummersl's答案转换为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) )

答案 32 :(得分:0)

一个简单的“思想和视觉友好”解决方案。

enter image description here

// 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']
));

答案 33 :(得分:0)

普通的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]
]);

答案 34 :(得分:0)

我注意到没有人发布允许传递函数来处理每个组合的解决方案,所以这是我的解决方案:

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))

输出:

Hello World!
Hello World?
Hello Planet!
Hello Planet?
Goodbye World!
Goodbye World?
Goodbye Planet!
Goodbye Planet?