查找JavaScript数组值的所有组合

时间:2010-12-02 02:20:18

标签: javascript algorithm

如何在N个可变长度的JavaScript数组中生成值的所有组合?

假设我有N个JavaScript数组,例如

var first = ['a', 'b', 'c', 'd'];
var second = ['e'];
var third =  ['f', 'g', 'h', 'i', 'j'];

(在这个例子中有三个数组,但它有N个数组用于解决问题。)

我想输出其值的所有组合,以产生

aef
aeg
aeh
aei
aej
bef
beg
....
dej

编辑:这是我工作的版本,使用ffriend接受的答案为基础。

var allArrays = [['a', 'b'], ['c', 'z'], ['d', 'e', 'f']];

 function allPossibleCases(arr) {
  if (arr.length === 0) {
    return [];
  } 
else if (arr.length ===1){
return arr[0];
}
else {
    var result = [];
    var allCasesOfRest = allPossibleCases(arr.slice(1));  // recur with the rest of array
    for (var c in allCasesOfRest) {
      for (var i = 0; i < arr[0].length; i++) {
        result.push(arr[0][i] + allCasesOfRest[c]);
      }
    }
    return result;
  }

}
var r=allPossibleCases(allArrays);
 //outputs ["acd", "bcd", "azd", "bzd", "ace", "bce", "aze", "bze", "acf", "bcf", "azf", "bzf"]

15 个答案:

答案 0 :(得分:53)

这不是排列,请参阅维基百科的permutations definitions

但是你可以通过递归

实现这一目标
var allArrays = [['a', 'b'], ['c'], ['d', 'e', 'f']]

function allPossibleCases(arr) {
  if (arr.length == 1) {
    return arr[0];
  } else {
    var result = [];
    var allCasesOfRest = allPossibleCases(arr.slice(1));  // recur with the rest of array
    for (var i = 0; i < allCasesOfRest.length; i++) {
      for (var j = 0; j < arr[0].length; j++) {
        result.push(arr[0][j] + allCasesOfRest[i]);
      }
    }
    return result;
  }

}

你也可以用循环来制作它,但它会有点棘手,需要实现你自己的堆栈模拟。

答案 1 :(得分:21)

您不需要递归或重度嵌套循环,甚至不需要在内存中生成/存储整个排列数组。

由于排列数是每个数组长度的乘积(调用此numPerms),因此可以创建一个函数getPermutation(n),它返回索引{{1}之间的唯一排列}和0根据numPerms - 1计算从中检索字符所需的索引。

这是怎么做到的?如果你想在每个包含:[0,1,2,... 9]的数组上创建排列,那么它非常简单......第245个排列(n = 245)是“245”,相当直观,或者:

n

问题的复杂性在于数组大小不同。我们可以通过将arrayHundreds[Math.floor(n / 100) % 10] + arrayTens[Math.floor(n / 10) % 10] + arrayOnes[Math.floor(n / 1) % 10] n/100等替换为其他除数来解决此问题。为此,我们可以很容易地预先计算一系列除数。在上面的例子中,100的除数等于n/10。因此,我们可以计算给定数组的除数是剩余数组长度的乘积。最后一个数组的除数总是1.另外,我们修改当前数组的长度而不是10的修正。

示例代码如下:

arrayTens.length * arrayOnes.length

答案 2 :(得分:18)

我建议使用简单的递归generator function,如下所示:

// Generate cartesian product of given iterables:
function* cartesian(head, ...tail) {
  let remainder = tail.length ? cartesian(...tail) : [[]];
  for (let r of remainder) for (let h of head) yield [h, ...r];
}

// Example:
const first  = ['a', 'b', 'c', 'd'];
const second = ['e'];
const third  = ['f', 'g', 'h', 'i', 'j'];

console.log(...cartesian(first, second, third));

答案 3 :(得分:16)

如果答案对我来说太难了。所以我的解决方案是:

var allArrays = new Array(['a', 'b'], ['c', 'z'], ['d', 'e', 'f']);

function getPermutation(array, prefix) {
    prefix = prefix || '';
    if (!array.length) {
        return prefix;
    }

    var result = array[0].reduce(function (result, value) {
        return result.concat(getPermutation(array.slice(1), prefix + value));
    }, []);
    return result;
}

console.log(getPermutation(allArrays));

答案 4 :(得分:6)

le_m的复制答案直接采用阵列数组:

<h1>Hello!</h1>  

希望能节省一些人的时间。

答案 5 :(得分:5)

您可以使用典型的回溯:

function cartesianProductConcatenate(arr) {
  var data = new Array(arr.length);
  return (function* recursive(pos) {
    if(pos === arr.length) yield data.join('');
    else for(var i=0; i<arr[pos].length; ++i) {
      data[pos] = arr[pos][i];
      yield* recursive(pos+1);
    }
  })(0);
}

我使用生成器函数来避免同时分配所有结果,但是如果你想要的话可​​以

[...cartesianProductConcatenate([['a', 'b'], ['c', 'z'], ['d', 'e', 'f']])];
// ["acd","ace","acf","azd","aze","azf","bcd","bce","bcf","bzd","bze","bzf"]

答案 6 :(得分:4)

您可以通过生成笛卡尔积来采用单行方法。

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

var items = [['a', 'b', 'c', 'd'], ['e'], ['f', 'g', 'h', 'i', 'j']],
    result = items.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; }

答案 7 :(得分:2)

如果您正在寻找可以处理具有任何项目类型的二维数组的流兼容函数,则可以使用以下函数。

const getUniqueCombinations = <T>(items : Array<Array<T>>, prepend : Array<T> = []) : Array<Array<T>> => {
    if(!items || items.length === 0) return [prepend];

    let out = [];

    for(let i = 0; i < items[0].length; i++){
        out = [...out, ...getUniqueCombinations(items.slice(1), [...prepend, items[0][i]])];
    }

    return out;
}

操作的可视化:

<强>在

[
    [Obj1, Obj2, Obj3],
    [Obj4, Obj5],
    [Obj6, Obj7]
]

<强>出:

[
    [Obj1, Obj4, Obj6 ],
    [Obj1, Obj4, Obj7 ],
    [Obj1, Obj5, Obj6 ],
    [Obj1, Obj5, Obj7 ],
    [Obj2, Obj4, Obj6 ],
    [Obj2, Obj4, Obj7 ],
    [Obj2, Obj5, Obj6 ],
    [Obj2, Obj5, Obj7 ],
    [Obj3, Obj4, Obj6 ],
    [Obj3, Obj4, Obj7 ],
    [Obj3, Obj5, Obj6 ],
    [Obj3, Obj5, Obj7 ]
]

答案 8 :(得分:2)

找到组合的最简单方法

const arr1= [ 'a', 'b', 'c', 'd' ];
const arr2= [ '1', '2', '3' ];
const arr3= [ 'x', 'y', ];

const all = [arr1, arr2, arr3];

const output = all.reduce((acc, cu) => { 
    let ret = [];
      acc.map(obj => {
        cu.map(obj_1 => {
          ret.push(obj + '-' + obj_1) 
        });
      });
      return ret;
   })

console.log(output);

答案 9 :(得分:1)

您可以创建2D数组并this answer。然后使用reduce在累加器数组和要迭代的当前数组中创建字符串组合,并将它们连接起来。

const data = [ ['a', 'b', 'c', 'd'], ['e'], ['f', 'g', 'h', 'i', 'j'] ]

const output = data.reduce((acc, cur) => acc.flatMap(c => cur.map(n => c + n)) )

console.log(output)

答案 10 :(得分:0)

这是一个改编自上述几个答案的版本,它按照OP中指定的顺序生成结果,并返回字符串而不是数组:

function *cartesianProduct(...arrays) {
  if (!arrays.length) yield [];
  else {
    const [tail, ...head] = arrays.reverse();
    const beginning = cartesianProduct(...head.reverse());
    for (let b of beginning) for (let t of tail) yield b + t;
  }
}

const first = ['a', 'b', 'c', 'd'];
const second = ['e'];
const third =  ['f', 'g', 'h', 'i', 'j'];
console.log([...cartesianProduct(first, second, third)])

答案 11 :(得分:0)

您也可以使用此功能:

const result = (arrayOfArrays) => arrayOfArrays.reduce((t, i) => { let ac = []; for (const ti of t) { for (const ii of i) { ac.push(ti + '/' + ii) } } return ac })

result([['a', 'b', 'c', 'd'], ['e'], ['f', 'g', 'h', 'i', 'j']])
// which will output [ 'a/e/f', 'a/e/g', 'a/e/h','a/e/i','a/e/j','b/e/f','b/e/g','b/e/h','b/e/i','b/e/j','c/e/f','c/e/g','c/e/h','c/e/i','c/e/j','d/e/f','d/e/g','d/e/h','d/e/i','d/e/j']

当然,您可以删除+ '/'中的ac.push(ti + '/' + ii),以消除最终结果中的斜线。而且,您可以使用forEach函数(加上for (... of ...)之前的分号)替换那些return ac,而无论您更喜欢哪个。

答案 12 :(得分:0)

没有递归的数组方法:

const combinations = [['1', '2', '3'], ['4', '5', '6'], ['7', '8']];
let outputCombinations = combinations[0]

combinations.slice(1).forEach(row => {
  outputCombinations = outputCombinations.reduce((acc, existing) =>
    acc.concat(row.map(item => existing + item))
  , []);
});

console.log(outputCombinations);

答案 13 :(得分:0)

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

的启发

const getAllCombinations = (arraysToCombine) => {
  const divisors = [];
  let permsCount = 1;
  for (let i = arraysToCombine.length - 1; i >= 0; i--) {
      divisors[i] = divisors[i + 1] ? divisors[i + 1] * arraysToCombine[i + 1].length : 1;
      permsCount *= (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 < permsCount; i++) {
      combinations.push(getCombination(i, arraysToCombine, divisors));
  }
  return combinations;
};

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

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

答案 14 :(得分:0)

let arr1 = [`a`, `b`, `c`];
let arr2 = [`p`, `q`, `r`];
let arr3 = [`x`, `y`, `z`];
let result = [];

arr1.forEach(e1 => {
    arr2.forEach(e2 => {
        arr3.forEach(e3 => {
            result[result.length] = e1 + e2 + e3;
        });
    });
});


console.log(result);
/*
output:
[
  'apx', 'apy', 'apz', 'aqx',
  'aqy', 'aqz', 'arx', 'ary',
  'arz', 'bpx', 'bpy', 'bpz',
  'bqx', 'bqy', 'bqz', 'brx',
  'bry', 'brz', 'cpx', 'cpy',
  'cpz', 'cqx', 'cqy', 'cqz',
  'crx', 'cry', 'crz'
]
*/