如何为两个数组的内容创建所有可能的组合?

IT技术 javascript arrays function combinations
2021-02-10 20:26:44

我有两个数组:

var array1 = ["A", "B", "C"];
var array2 = ["1", "2", "3"];

如何设置另一个数组以包含上述所有组合,以便:

var combos = ["A1", "A2", "A3", "B1", "B2", "B3", "C1", "C2", "C3"];
6个回答

或者,如果您想使用任意数量的任意大小的数组创建组合...(我确定您可以递归地执行此操作,但是由于这不是求职面试,我改为使用迭代“ odometer”为此......它根据每个数组的长度增加一个“数字”,每个数字都是一个“base-n”数字)......例如......

combineArrays([ ["A","B","C"],
                ["+", "-", "*", "/"],
                ["1","2"] ] )

...返回...

[
   "A+1","A+2","A-1", "A-2",
   "A*1", "A*2", "A/1", "A/2", 
   "B+1","B+2","B-1", "B-2",
   "B*1", "B*2", "B/1", "B/2", 
   "C+1","C+2","C-1", "C-2",
   "C*1", "C*2", "C/1", "C/2"
]

...每一个都对应一个“里程表”值,从每个数组中选择一个索引......

[0,0,0], [0,0,1], [0,1,0], [0,1,1]
[0,2,0], [0,2,1], [0,3,0], [0,3,1]
[1,0,0], [1,0,1], [1,1,0], [1,1,1]
[1,2,0], [1,2,1], [1,3,0], [1,3,1]
[2,0,0], [2,0,1], [2,1,0], [2,1,1]
[2,2,0], [2,2,1], [2,3,0], [2,3,1]

“里程表”方法允许您轻松生成所需的输出类型,而不仅仅是像我们这里那样的连接字符串。除此之外,通过避免递归,我们避免了——我敢说吗?--堆栈溢出...

function combineArrays( array_of_arrays ){

    // First, handle some degenerate cases...

    if( ! array_of_arrays ){
        // Or maybe we should toss an exception...?
        return [];
    }

    if( ! Array.isArray( array_of_arrays ) ){
        // Or maybe we should toss an exception...?
        return [];
    }

    if( array_of_arrays.length == 0 ){
        return [];
    }

    for( let i = 0 ; i < array_of_arrays.length; i++ ){
        if( ! Array.isArray(array_of_arrays[i]) || array_of_arrays[i].length == 0 ){
            // If any of the arrays in array_of_arrays are not arrays or zero-length, return an empty array...
            return [];
        }
    }

    // Done with degenerate cases...

    // Start "odometer" with a 0 for each array in array_of_arrays.
    let odometer = new Array( array_of_arrays.length );
    odometer.fill( 0 ); 

    let output = [];

    let newCombination = formCombination( odometer, array_of_arrays );

    output.push( newCombination );

    while ( odometer_increment( odometer, array_of_arrays ) ){
        newCombination = formCombination( odometer, array_of_arrays );
        output.push( newCombination );
    }

    return output;
}/* combineArrays() */


// Translate "odometer" to combinations from array_of_arrays
function formCombination( odometer, array_of_arrays ){
    // In Imperative Programmingese (i.e., English):
    // let s_output = "";
    // for( let i=0; i < odometer.length; i++ ){
    //    s_output += "" + array_of_arrays[i][odometer[i]]; 
    // }
    // return s_output;

    // In Functional Programmingese (Henny Youngman one-liner):
    return odometer.reduce(
      function(accumulator, odometer_value, odometer_index){
        return "" + accumulator + array_of_arrays[odometer_index][odometer_value];
      },
      ""
    );
}/* formCombination() */

function odometer_increment( odometer, array_of_arrays ){

    // Basically, work you way from the rightmost digit of the "odometer"...
    // if you're able to increment without cycling that digit back to zero,
    // you're all done, otherwise, cycle that digit to zero and go one digit to the
    // left, and begin again until you're able to increment a digit
    // without cycling it...simple, huh...?

    for( let i_odometer_digit = odometer.length-1; i_odometer_digit >=0; i_odometer_digit-- ){ 

        let maxee = array_of_arrays[i_odometer_digit].length - 1;         

        if( odometer[i_odometer_digit] + 1 <= maxee ){
            // increment, and you're done...
            odometer[i_odometer_digit]++;
            return true;
        }
        else{
            if( i_odometer_digit - 1 < 0 ){
                // No more digits left to increment, end of the line...
                return false;
            }
            else{
                // Can't increment this digit, cycle it to zero and continue
                // the loop to go over to the next digit...
                odometer[i_odometer_digit]=0;
                continue;
            }
        }
    }/* for( let odometer_digit = odometer.length-1; odometer_digit >=0; odometer_digit-- ) */

}/* odometer_increment() */
哇,这太棒了,正是我所需要的。一个函数,用于在可变数量的数组之间创建所有可能的组合,每个数组包含可变数量的元素。
2021-03-17 20:26:44
你是个天才。简直就是救了我的命。这应该是公认的答案,因为它涵盖了 OP 情况以及任何其他情况。
2021-03-17 20:26:44
出色地解决了,我已经绞尽脑汁好几个小时了,想不出任何接近于此的东西。我扩展了您的版本,通过array_prefixes在函数调用中添加一个新参数作为最后一个参数,然后return accumulator + ' ' + array_prefixes[odometer_index] + ': ' + array_of_arrays[odometer_index][odometer_value];在每个值之前添加名称,从而允许使用自定义名称为每个数组的值添加前缀
2021-03-18 20:26:44

以防万一有人正在寻找Array.map解决方案

var array1=["A","B","C"];

var array2=["1","2","3","4"];

console.log(array1.flatMap(d => array2.map(v => d + v)))

需要注意的一点是 IE 不支持 flatMap,否则这看起来是一个干净的解决方案
2021-04-10 20:26:44

这种形式的循环

combos = [] //or combos = new Array(2);

for(var i = 0; i < array1.length; i++)
{
     for(var j = 0; j < array2.length; j++)
     {
        //you would access the element of the array as array1[i] and array2[j]
        //create and array with as many elements as the number of arrays you are to combine
        //add them in
        //you could have as many dimensions as you need
        combos.push(array1[i] + array2[j])
     }
}
不完全确定您要做什么,但也许 flatMap 是您所需要的?你能提供更多信息吗?
2021-03-16 20:26:44
我不能用 .map 做到这一点,知道为什么吗?
2021-04-05 20:26:44

for在所有答案中看到很多循环......

这是我想出的递归解决方案,它将通过从每个数组中取出 1 个元素来找到 N 个数组的所有组合:

const array1=["A","B","C"]
const array2=["1","2","3"]
const array3=["red","blue","green"]

const combine = ([head, ...[headTail, ...tailTail]]) => {
  if (!headTail) return head

  const combined = headTail.reduce((acc, x) => {
    return acc.concat(head.map(h => `${h}${x}`))
  }, [])

  return combine([combined, ...tailTail])
}

console.log('With your example arrays:', combine([array1, array2]))
console.log('With N arrays:', combine([array1, array2, array3]))

停止搜索。这是最好的解决方案。
2021-03-18 20:26:44
而不是使用reduceconcat我认为您还可以使用平面图和地图(这也将组合按 OP 的请求顺序放置): const combined = head.flatMap((a) => headTail.map((b) => `${a}${b}`));
2021-03-19 20:26:44
非常干净的解决方案!
2021-03-30 20:26:44

假设您使用的是最近支持的 Web 浏览器Array.forEach

var combos = [];
array1.forEach(function(a1){
  array2.forEach(function(a2){
    combos.push(a1 + a2);
  });
});

如果您没有forEach,那么在没有它的情况下重写它是一个足够简单的练习。正如其他人之前已经证明的那样,不这样做也有一些性能优势......(虽然我认为不久之后,常见的 JavaScript 运行时将优化掉任何当前的优势,否则这样做。)

@Dingredient - 并不真正依赖于浏览器,而是支持的 JavaScript 版本。如果forEach不起作用,请改用 rubixibuc 的答案。
2021-03-18 20:26:44
也可以用 for 循环替换 forEach。
2021-03-27 20:26:44
没有浏览器...我将使用 phonegap 将其变成原生 iphone 应用程序。那还能用吗?
2021-04-08 20:26:44