JavaScript中多个数组的笛卡尔积

JavaScript中多个数组的笛卡尔积,javascript,algorithm,functional-programming,Javascript,Algorithm,Functional Programming,如何在JavaScript中实现多个数组的笛卡尔积 例如, cartesian([1, 2], [10, 20], [100, 200, 300]) 应该回来 [ [1, 10, 100], [1, 10, 200], [1, 10, 300], [2, 10, 100], [2, 10, 200] ... ] 社区似乎认为这很琐碎,或者很容易找到一个参考实现,经过简单的检查,我不能,或者可能只是因为我喜欢重新发明轮子或者解决课堂上的编程问题,不管怎样,这都是你的幸

如何在JavaScript中实现多个数组的笛卡尔积

例如,

cartesian([1, 2], [10, 20], [100, 200, 300]) 
应该回来

[
  [1, 10, 100],
  [1, 10, 200],
  [1, 10, 300],
  [2, 10, 100],
  [2, 10, 200]
  ...
]

社区似乎认为这很琐碎,或者很容易找到一个参考实现,经过简单的检查,我不能,或者可能只是因为我喜欢重新发明轮子或者解决课堂上的编程问题,不管怎样,这都是你的幸运日:

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]
   ]
函数cartProd(paramArray){
函数addTo(curr,args){
var i,收到,
剩余=参数切片(1),
last=!rest.length,
结果=[];
对于(i=0;i>日志(cartProd([1,2],[10,20],[100200300]);
>> [
[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从循环中去掉,并且有两个独立的循环来获得一些好处,因为它在技术上是恒定的,你可以帮助进行分支预测和所有的混乱,但是这一点在javascript中是没有意义的


任何人,请欣赏-ck

下面是一个使用
reduce
flatte
解决问题的函数式解决方案(没有任何可变变量)),由
下划线.js
提供:

函数cartesianProductOf(){
返回u0.reduce(参数,函数(a,b){
返回展平(u.map(a,函数(x){
返回映射(b,函数(y){
返回x.concat([y]);
});
}),对);
}, [ [] ]);
}
//[1,3,“a”],[1,3,“b”],[1,4,“a”],[1,4,“b”],[2,3,“a”],[2,3,“b”],[2,4,“a”],[2,4,“b”]
log(cartesianProductOf([1,2],[3,4],'a'])

含有lodash的咖啡脚本版本:

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

下面是一个简单的递归解决方案:

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))
import { xprod, flatten } from 'ramda';

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

函数cartesianProduct(a){//a=数组的数组
变量i,j,l,m,a1,o=[];
如果(!a | | a.length==0),则返回a;
a1=a.splice(0,1)[0];//a的第一个数组
a=卡特尔产品(a);
对于(i=0,l=a1.length;i//[1,10100]、[1,10200]、[1,10300]、[1,20100]、[1,20200]、[1,20300]、[2,10100]、[2,10200]、[2,20100]、[2,20200]、[2,20300]。
使用ES6发电机的典型回溯

函数笛卡尔产品(…数组){
让当前=新数组(arrays.length);
返回(函数*回溯(索引){
如果(index==arrays.length)产生current.slice();
else for(让数组的数目[索引]){
当前[索引]=num;
收益率*回溯(指数+1);
}
})(0);
}
对于(cartesianProduct([1,2]、[10,20]、[100200300])的let项){
console.log('['+item.join(',')+']');
}

div.as-console-wrapper{max height:100%;}
一种非递归方法,可在将产品实际添加到结果集中之前,对其进行过滤和修改。请注意使用.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;
    }
函数交叉积(数组、行测试、行操作){
//计算结果中所需的元素数
var result_elems=1,row_size=arrays.length;
map(函数(数组){
结果元素*=数组长度;
});
var temp=新数组(结果元素),结果=[];
//检查每个数组,并将适当的元素添加到temp的每个元素中
var比例系数=结果要素;
map(函数(数组)
{
var set_elems=array.length;
比例系数/=集合元素;
对于(变量i=result\u elems-1;i>=0;i--){
温度[i]=(温度[i]?温度[i]:[]);
var pos=i/比例系数%set要素;
//处理索引的浮点结果,这需要一些实验

if(pos<1 | | pos%1这里是@viebel代码的一个修改版本,使用纯Javascript,不使用任何库:

功能卡特尔产品(arr){
返回arr.reduce(函数(a,b){
返回a.map(函数(x){
返回b.map(函数(y){
返回x.concat([y]);
})
}).reduce(函数(a,b){返回a.concat(b)},[])
}, [[]])
}
var a=卡特尔产品([[1,2,3],[4,5,6],[7,8],[9,10]);

console.log(JSON.stringify(a));
这里有一种递归方法,它使用ECMAScript 2015,因此您不必一次创建所有元组:

函数*cartesian(){
让数组=参数;
函数*doCartesian(i,prod){
if(i==arrays.length){
产量产品;
}否则{
for(设j=0;j<数组[i]。长度;j++){
收益率*doCartesian(i+1,prod.concat([arrays[i][j]]);
}
}
}
收益率*doCartesian(0,[]);
}
log(JSON.stringify(Array.from(笛卡尔([1,2],[10,20],[100200300]));

console.log(JSON.stringify(Array.from(笛卡尔([1],[2]],[10,20],[100200300]));
当任何输入数组包含数组项时,此主题下的一些答案都会失败。您最好检查一下

无论如何,不需要下划线和lodash。我相信这一个应该用纯JSES6来实现,尽可能地发挥它的功能

这段代码使用一个reduce和一个嵌套映射,只是为了得到两个数组的笛卡尔乘积
const cartesian =
  (...a) => a.reduce((a, b) => a.flatMap(d => b.map(e => [d, e].flat())));
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 ] ]
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?
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);
});
cartesianProduct: 427.378ms
cartesianProduct_rsp: 1710.829ms
cartesianProduct_sebnukem: 593.351ms
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))), []);
// 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']
));
result = data.reduce(
    (a, b) => a.reduce(
        (r, v) => r.concat(b.map(w => [].concat(v, w))),
        []
    )
);
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 ] ];
*/
data.reduce((a, b) => a.flatMap(x => b.map(y => [...x, y])), [[]]);
f=(a,b,c)=>a.flatMap(ai=>b.flatMap(bi=>c.map(ci=>[ai,bi,ci])))
f(a,a,a)
f=(a,b,c)=>a.flatMap(a1=>a.flatMap(a2=>a.map(a3=>[a1,a2,a3])))
import { xprod, flatten } from 'ramda';

const cartessian = (...xs) => xs.reduce(xprod).map(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);