【问题标题】:Find all permutations of 2 arrays in JS在 JS 中查找 2 个数组的所有排列
【发布时间】:2019-05-22 08:44:42
【问题描述】:

我试图找到这样的 2 个数组的每个排列:

// input
lowerWords = ['one', 'two', 'three' ] 
upperWords = [ 'ONE', 'TWO', 'THREE' ]

// output
keywords = {
  'one two three': true,
  'ONE two three': true,
  'ONE TWO three': true,
  'ONE TWO THREE': true,
  'ONE two THREE': true,
  'one TWO three': true,
  'one two THREE': true,
  'one TWO THREE': true,
}

它应该与 3 个以上的项目一起工作,两个数组的长度总是相同的。这是我的代码:

const keywords = {}
const lowerWords = ['one', 'two', 'three' ] 
const upperWords = [ 'ONE', 'TWO', 'THREE' ]
const wordCount = lowerWords.length

let currentWord = 0
let currentWords = [...upperWords]
while (currentWord < wordCount) {
  currentWords[currentWord] = lowerWords[currentWord]
  let keyword = currentWords.join(' ')
  keywords[keyword] = true
  currentWord++
}

currentWord = 0
currentWords = [...lowerWords]
while (currentWord < wordCount) {
  currentWords[currentWord] = upperWords[currentWord]
  let keyword = currentWords.join(' ')
  keywords[keyword] = true
  currentWord++
}

结果少了一些

ONE TWO THREE: true
ONE TWO three: true
ONE two three: true
one TWO THREE: true
one two THREE: true
one two three: true

【问题讨论】:

  • 在您想要的答案中,第 3 次和第 9 次重复。
  • @Tigger 不,这些是 2 的组合,我需要每个项目的排列
  • @JackOfAshes-MohitGawande 是的,这是一个错误
  • 您不是在寻找排列(与顺序有关),而是寻找[one, ONE][two, TWO][three, THREE]cartesian product。你可以通过转置你的输入来获得这些。
  • @Bergi 我不确定您的建议是否适用于任意输入(请参阅我在 Nina Scholz 的回答下的评论)。

标签: javascript algorithm


【解决方案1】:

您可以转置数组以获取对数组,然后获取对的所有组合。

const
    transpose = array => array.reduce((r, a) => a.map((v, i) => [...(r[i] || []), v]), []),
    combinations = array => array.reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []));

var lowerWords = ['one', 'two', 'three'],
    upperWords = ['ONE', 'TWO', 'THREE'],
    pairs = transpose([lowerWords, upperWords]),
    result = combinations(pairs);
    
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

  • (对于带有transpose([['ONE','TWO','THREE'],['one','two'], [1,2,3,4]]) 的普通版本与mine 比较,这会失败。)
  • transpose 转置相同长度的数组。
  • @גלעדברקן 目前还不清楚您希望从该输入中获得什么输出。
  • @Bergi 我实现了一个这样的清晰度。
  • @גלעדברקן 好吧,使用转置的输入格式,这真的没有意义。如果需要这种输出,应该只调用cartesian([['one', 'ONE', 1], ['two','TWO', 2], ['three', 3], [4]])
【解决方案2】:

我想试试看。我使用二进制来获得可能的组合,因为这个问题需要一个 base 2 解决方案:

const low = ["one", "two", "three"];
const up = ["ONE", "TWO", "THREE"];

const words = [low, up]
const len = words[0].length


function getCombinations(noOfArrays, len) {
  var temp, newCombo, combos = [];
  for (var i = 0; i < (noOfArrays ** len); i++) {
    temp = new Array(len).fill(0)
    newCombo = i.toString(noOfArrays).split('');
    newCombo.forEach((el, i) => temp[temp.length - newCombo.length + i] = +el);
    combos.push(temp);
  }
  return combos;
}

function setCombinations(combos) {
  return combos.map(combo => combo.map((el, i) => words[el][i]))
}


var combos = getCombinations(words.length, len)
combos = setCombinations(combos)


console.log(combos)

循环解释:

1. temp = new Array(len).fill(0)
2. newCombo = i.toString(2).split("");
3. newCombo.forEach((el, i) => temp[temp.length - newCombo.length + i] = +el);
  1. 创建临时数组[0,0,0]
  2. 获取循环编号 (i) 并将其转换为二进制,例如:
1 -> 1
2 -> 10
3 -> 11
4 -> 100
etc...

然后将二进制文件拆分成数组100 -&gt; [1,0,0]

  1. 然后将每个元素推入新数组。这给将 1 和 2 元素数组 (10 -&gt; [1,0]) 推入数组的后面带来了问题。我使用temp.length - newCombo.length + i 解决了这个问题。

然后该函数返回:

[ 0, 0, 0 ]
[ 0, 0, 1 ]
[ 0, 1, 0 ]
[ 0, 1, 1 ]
[ 1, 0, 0 ]
[ 1, 0, 1 ]
[ 1, 1, 0 ]
[ 1, 1, 1 ]

然后,我可以映射每个组合,并根据值获取每个数组,并通过循环索引获取单词('one' 或 'ONE')。

请注意,此代码适用于多个数组,只要数组长度相同。

【讨论】:

  • 您好,我已收藏这个问题,稍后再回来提供答案。我的答案与您的非常相似(我独立提出,我发誓:))。我还添加了它的通用版本。希望你不要介意。
  • @adiga 没问题,伟大的思想都一样:p 我喜欢这种方法,因为它非常可扩展,因为您可以更改将索引转换为用户提供多少数组的基数。我也没有考虑过使用 padStart,做得很好?
【解决方案3】:

您总共需要获得 2 ^ 3 个组合。如果您从 2 个数组创建一个 2D 矩阵,下表表示应从中获取项目的行号。

0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1

如果你分析组合的索引,每一个都是从 0 到 2 ^ 3 的二进制数,前导零。

所以,你可以

  • 从 0 循环到 8
  • 使用toString(2)创建二进制数
  • 使用padStart 添加前导零
  • split每个数字得到一个数组
  • matrix[digit-from-binary][position-of-each-split]获取每个项目
  • join 带有 ' ' 分隔符的项目数组以获取 key
  • 将键添加到输出对象

工作 sn-p:

function getAllCombinations(matrix) {
  const combinations = 2 ** 3,
        output = {};
  
  for(let i = 0; i < combinations; i++) {
      const key = i.toString(2)
                    .padStart(3, 0)
                    .split('')
                    .map((n, j) => matrix[n][j])
                    .join(" ")
                    
      output[key] = true;
  }
  
  return output
}

console.log(getAllCombinations([['one', 'two', 'three' ],[ 'ONE', 'TWO', 'THREE' ]]))

您可以将其推广到m x n 矩阵。您需要将其转换为base-mpadStart,而不是将其转换为长度n

function getAllCombinations(matrix) {
  const rows = matrix.length,
        columns = matrix[0].length,
        combinations = rows ** columns;

  return Array.from({ length: combinations },
    (_, i) => i.toString(rows)
                .padStart(columns, 0)
                .split('')
                .map((n, j) => matrix[n][j])
  )
}

console.log(JSON.stringify(
    getAllCombinations( [[1, 2, 3], [4, 5, 6], [7, 8, 9]] ) // 3 x 3 matrix
));

console.log(JSON.stringify(
    getAllCombinations( [[1, 2], [3, 4], [5, 6], [7, 8]] ) // 4 x 2 matrix
));
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

    【解决方案4】:

    这是一个基于我的另一个 answer 的通用版本,它处理可变数量的长度可变的输入数组:

    const g = (arrs, i=0, comb=[]) =>
      !arrs.some(arr => i < arr.length)
      ? [comb]
      : arrs.reduce((acc, arr) => 
          i >= arr.length ? acc :
          acc.concat(g(arrs, i + 1, comb.slice().concat(arr[i])))
        , [])
        
    
    // Example output
    let input = [['ONE','TWO','THREE'], ['one','two'], [1,2,3,4]]
    
    let str = ''
    for (let line of g(input))
      str += JSON.stringify(line) + '\n'
    console.log(str)

    【讨论】:

      【解决方案5】:

      我们可以直接列举这些。这是一种算法:

      If we've reached the end of
      the list, return the combination
      the current recursion branch is
      building.
      
      Otherwise, create a new branch
      that picks the next item from B,
      while the current branch picks
      the next item from A.
      

      JavaScript 代码:

      function f(A, B, i=0, comb=[]){
        return i == A.length
               ? [comb]
               : f(A, B, i + 1, comb.concat(A[i])).concat(
                 f(A, B, i + 1, comb.slice().concat(B[i])))
      }
      
      console.log(JSON.stringify(f(['one','two','three'], ['ONE','TWO','THREE'])))

      【讨论】:

        【解决方案6】:

        以下代码应该适用于您的递归方法:

        const lowerWords = ['one', 'two', 'three']
        const upperWords = ['ONE', 'TWO', 'THREE']
        
        let result = {};
        
        function getCombinations(index, caseType, arr) {
          if (index == 3) {
            arr[index] = (caseType == 'lower' ? lowerWords : upperWords)[index];
            result[arr.join(' ')] = true
            return
          }
          arr[index] = (caseType == 'lower' ? lowerWords : upperWords)[index];
          getCombinations(index + 1, 'lower', arr);
          getCombinations(index + 1, 'upper', arr);
        }
        getCombinations(0, 'lower', [])
        getCombinations(0, 'upper', [])
        
        console.log('resultresult', result)

        【讨论】:

          猜你喜欢
          • 2015-12-31
          • 2017-01-31
          • 2010-09-23
          • 1970-01-01
          • 2015-06-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多