Encontrar todas as combinações de valores de matriz JavaScript

Como posso produzir todas as combinações dos valores no número N de matrizes JavaScript de comprimentos variables?

Digamos que eu tenha um número N de matrizes JavaScript, por exemplo

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

(Três matrizes neste exemplo, mas seu número N de matrizes para o problema.)

E eu quero produzir todas as combinações de seus valores, para produzir

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

EDIT: Aqui está a versão que eu comecei a trabalhar, usando a resposta aceita de ffriend como base.

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

Isto não é permutações, veja as definições de permutações da Wikipedia.

Mas você pode conseguir isso com recursion :

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

Você também pode fazê-lo com loops, mas será um pouco complicado e exigirá a implementação de seu próprio análogo de stack.

Você não precisa de recursion, nem de loops muito nesteds, ou mesmo de gerar / armazenar toda a matriz de permutações na memory.

Como o número de permutações é o produto dos comprimentos de cada uma das matrizes (chame este numPerms ), você pode criar uma function getPermutation(n) que retorna uma permutação única entre o índice 0 e numPerms - 1 calculando os índices necessários recuperar seus personagens, baseado em n .

Como isso é feito? Se você pensar em criar permutações em matrizes contendo cada uma: [0, 1, 2, … 9] é muito simples … a 245ª permutação (n = 245) é “245”, intuitivamente, ou:

 arrayHundreds[Math.floor(n / 100) % 10] + arrayTens[Math.floor(n / 10) % 10] + arrayOnes[Math.floor(n / 1) % 10] 

A complicação do seu problema é que os tamanhos das matrizes são diferentes. Podemos contornar isso substituindo o n/100 , n/10 , etc … por outros divisores. Podemos facilmente pré-calcular uma matriz de divisores para este propósito. No exemplo acima, o divisor de 100 era igual a arrayTens.length * arrayOnes.length . Portanto, podemos calcular o divisor de uma determinada matriz para ser o produto dos comprimentos das matrizes restantes. O último array sempre tem um divisor de 1. Além disso, ao invés de mods por 10, modamos o tamanho do array atual.

Exemplo de código está abaixo:

 var allArrays = [first, second, third, ...]; // Pre-calculate divisors var divisors = []; for (var i = allArrays.length - 1; i >= 0; i--) { divisors[i] = divisors[i + 1] ? divisors[i + 1] * allArrays[i + 1].length : 1; } function getPermutation(n) { var result = "", curArray; for (var i = 0; i < allArrays.length; i++) { curArray = allArrays[i]; result += curArray[Math.floor(n / divisors[i]) % curArray.length]; } return result; } 

Respostas fornecidas parece muito difícil para mim. Então minha solução é:

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

Eu sugiro uma function geradora recursiva simples como segue:

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

Você pode usar um backtracking típico:

 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 

Eu usei funções geradoras para evitar alocar todos os resultados simultaneamente, mas se você quiser, pode

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

Cópia da Resposta de le_m para receber Array of Arrays diretamente:

 function *combinations(arrOfArr) { let [head, ...tail] = arrOfArr let remainder = tail.length ? combinations(tail) : [[]]; for (let r of remainder) for (let h of head) yield [h, ...r]; } 

Espero que isso economize o tempo de alguém.

Se você estiver procurando por uma function compatível com stream que possa manipular matrizes bidimensionais com qualquer tipo de item, você pode usar a function abaixo.

 const getUniqueCombinations = (items : Array>, prepend : Array = []) : Array> => { 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; } 

Uma visualização da operação:

dentro:

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

Fora:

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

Aqui está uma versão adaptada do par de respostas acima, que produz os resultados na ordem especificada no OP, e retorna strings ao invés de arrays:

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

Você poderia usar esta function também:

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

É claro que você pode remover o + '/' em ac.push(ti + '/' + ii) para eliminar a barra do resultado final. E você pode substituí-los for (... of ...) por funções forEach (mais o ponto e vírgula respectivo antes do return ac ), qualquer que seja a pessoa com a qual você esteja mais confortável.

A maneira mais fácil de encontrar as combinações

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