Javascript - 在单个数组中生成所有元素组合(成对)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/43241174/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 01:46:19  来源:igfitidea点击:

Javascript - Generating all combinations of elements in a single array (in pairs)

javascriptarraysalgorithmcombinations

提问by dhdz

I've seen several similar questions about how to generate all possible combinations of elements in an array. But I'm having a very hard time figuring out how to write an algorithm that will only output combination pairs. Any suggestions would be super appreciated!

我已经看到了几个关于如何生成数组中元素的所有可能组合的类似问题。但是我很难弄清楚如何编写只输出组合的算法。任何建议将不胜感激!

Starting with the following array (with N elements):

从以下数组开始(有 N 个元素):

var array = ["apple", "banana", "lemon", "mango"];

And getting the following result:

并得到以下结果:

var result = [
   "apple banana"
   "apple lemon"
   "apple mango"
   "banana lemon"
   "banana mango"
   "lemon mango"
];

I was trying out the following approach but this results in all possible combinations, instead only combination pairs.

我正在尝试以下方法,但这会导致所有可能的组合,而只是组合对。

var letters = splSentences;
var combi = [];
var temp= "";
var letLen = Math.pow(2, letters.length);

for (var i = 0; i < letLen ; i++){
    temp= "";
    for (var j=0;j<letters.length;j++) {
        if ((i & Math.pow(2,j))){ 
            temp += letters[j]+ " "
        }
    }
    if (temp !== "") {
        combi.push(temp);
    }
}

回答by Mike Cluck

A simple way would be to do a double for loop over the array where you skip the first ielements in the second loop.

一种简单的方法是对数组执行双循环,在其中跳过i第二个循环中的第一个元素。

let array = ["apple", "banana", "lemon", "mango"];
let results = [];

// Since you only want pairs, there's no reason
// to iterate over the last element directly
for (let i = 0; i < array.length - 1; i++) {
  // This is where you'll capture that last value
  for (let j = i + 1; j < array.length; j++) {
    results.push(`${array[i]} ${array[j]}`);
  }
}

console.log(results);

Rewritten with ES5:

用 ES5 重写:

var array = ["apple", "banana", "lemon", "mango"];
var results = [];

// Since you only want pairs, there's no reason
// to iterate over the last element directly
for (var i = 0; i < array.length - 1; i++) {
  // This is where you'll capture that last value
  for (var j = i + 1; j < array.length; j++) {
    results.push(array[i] + ' ' + array[j]);
  }
}

console.log(results);

回答by trincot

Here are some functional programmingsolutions:

以下是一些函数式编程解决方案:

Using EcmaScript2019's flatMap:

使用 EcmaScript2019 的flatMap

var array = ["apple", "banana", "lemon", "mango"];

var result = array.flatMap(
    (v, i) => array.slice(i+1).map( w => v + ' ' + w )
);

console.log(result);

Before the introduction of flatMap(my answer in 2017), you would go for reduceor [].concat(...)in order to flatten the array:

在引入flatMap(我在 2017 年的回答)之前,您会选择reduce[].concat(...)为了展平数组:

var array = ["apple", "banana", "lemon", "mango"];

var result = array.reduce( (acc, v, i) =>
    acc.concat(array.slice(i+1).map( w => v + ' ' + w )),
[]);

console.log(result);

Or:

或者:

var array = ["apple", "banana", "lemon", "mango"];

var result = [].concat(...array.map( 
    (v, i) => array.slice(i+1).map( w => v + ' ' + w ))
);

console.log(result);

回答by nhnghia

Although solutions have been found, I post here an algorithm for general case to find all combinations size nof m (m>n)elements. In your case, we have n=2and m=4.

尽管方案已经找到,我张贴在这里一般情况下找到所有组合大小的算法nm (m>n)元素。在您的情况下,我们有n=2m=4

const result = [];
result.length = 2; //n=2

function combine(input, len, start) {
  if(len === 0) {
    console.log( result.join(" ") ); //process here the result
    return;
  }
  for (let i = start; i <= input.length - len; i++) {
    result[result.length - len] = input[i];
    combine(input, len-1, i+1 );
  }
}

const array = ["apple", "banana", "lemon", "mango"];    
combine( array, result.length, 0);

回答by Peter

Using mapand flatMapthe following can be done (flatMapis only supported on chrome and firefox)

使用mapflatMap可以完成以下操作(flatMap仅在chrome 和 firefox上支持)

var array = ["apple", "banana", "lemon", "mango"]
array.flatMap(x => array.map(y => x !== y ? x + ' ' + y : null)).filter(x => x)

回答by Oliver Coleman

I ended up writing a general solution to this problem, which is functionally equivalent to nhnghia'sanswer, but I'm sharing it here as I think it's easier to read/follow and is also full of comments describing the algorithm.

我最终为这个问题写了一个通用的解决方案,它在功能上等同于nhnghia 的答案,但我在这里分享它,因为我认为它更容易阅读/遵循,并且也充满了描述算法的评论。


/**
 * Generate all combinations of an array.
 * @param {Array} sourceArray - Array of input elements.
 * @param {number} comboLength - Desired length of combinations.
 * @return {Array} Array of combination arrays.
 */
function generateCombinations(sourceArray, comboLength) {
  const sourceLength = sourceArray.length;
  if (comboLength > sourceLength) return [];

  const combos = []; // Stores valid combinations as they are generated.

  // Accepts a partial combination, an index into sourceArray, 
  // and the number of elements required to be added to create a full-length combination.
  // Called recursively to build combinations, adding subsequent elements at each call depth.
  const makeNextCombos = (workingCombo, currentIndex, remainingCount) => {
    const oneAwayFromComboLength = remainingCount == 1;

    // For each element that remaines to be added to the working combination.
    for (let sourceIndex = currentIndex; sourceIndex < sourceLength; sourceIndex++) {
      // Get next (possibly partial) combination.
      const next = [ ...workingCombo, sourceArray[sourceIndex] ];

      if (oneAwayFromComboLength) {
        // Combo of right length found, save it.
        combos.push(next);
      }
      else {
        // Otherwise go deeper to add more elements to the current partial combination.
        makeNextCombos(next, sourceIndex + 1, remainingCount - 1);
      }
        }
  }

  makeNextCombos([], 0, comboLength);
  return combos;
}

回答by Konstantinos

Try this: https://jsfiddle.net/e2dLa9v6/

试试这个:https: //jsfiddle.net/e2dLa9v6/

var array = ["apple", "banana", "lemon", "mango"];
var result = [];

for(var i=0;i<array.length-1;i++){
    for(var j=i+1;j<array.length;j++){
    result.push(array[i]+" "+array[j]);
  }
}
for(var i=0;i<result.length;i++){
    alert(result[i]);
}

回答by Kolja

Generating combinations of elements in an array is a lot like counting in a numeral system, where the base is the number of elements in your array (if you account for the leading zeros that will be missing).

在数组中生成元素的组合很像在数字系统中计数,其中基数是数组中元素的数量(如果您考虑将丢失的前导零)。

This gives you all the indices to your array (concatenated):

这为您提供了数组的所有索引(连接):

arr = ["apple", "banana", "lemon", "mango"]
base = arr.length

idx = [...Array(Math.pow(base, base)).keys()].map(x => x.toString(base))

You are only interested in pairs of two, so restrict the range accordingly:

您只对成对的两个感兴趣,因此相应地限制范围:

range = (from, to) = [...Array(to).keys()].map(el => el + from)
indices = range => range.map(x => x.toString(base).padStart(2,"0"))

indices( range( 0, Math.pow(base, 2))) // range starts at 0, single digits are zero-padded.

Now what's left to do is map indices to values.

现在剩下要做的是将索引映射到值。

As you don't want elements paired with themselves and order doesn't matter, those need to be removed, before mapping to the final result.

由于您不希望元素与其自身配对并且顺序无关紧要,因此在映射到最终结果之前需要删除这些元素。

const range = (from, to) => [...Array(to).keys()].map(el => el + from)
const combinations = arr => {
  const base = arr.length
  return range(0, Math.pow(base, 2))
    .map(x => x.toString(base).padStart(2, "0"))
    .filter(i => !i.match(/(\d)/) && i === i.split('').sort().join(''))
    .map(i => arr[i[0]] + " " + arr[i[1]])
}

console.log(combinations(["apple", "banana", "lemon", "mango"]))

With more than ten elements, toString()will return letters for indices; also, this will only work with up to 36 Elements.

超过十个元素,toString()将返回索引的字母;此外,这只适用于最多 36 个元素。

回答by JxDarkAngel

In my case, I wanted to get the combinations as follows, based on the size range of the array:

就我而言,我想根据数组的大小范围获得如下组合:

function getCombinations(valuesArray: String[])
{

var combi = [];
var temp = [];
var slent = Math.pow(2, valuesArray.length);

for (var i = 0; i < slent; i++)
{
    temp = [];
    for (var j = 0; j < valuesArray.length; j++)
    {
        if ((i & Math.pow(2, j)))
        {
            temp.push(valuesArray[j]);
        }
    }
    if (temp.length > 0)
    {
        combi.push(temp);
    }
}

combi.sort((a, b) => a.length - b.length);
console.log(combi.join("\n"));
return combi;
}

Example:

例子:

// variable "results" stores an array with arrays string type
let results = getCombinations(['apple', 'banana', 'lemon', ',mango']);

Output in console:

控制台输出:

enter image description here

在此处输入图片说明

The function is based on the logic of the following documentation, more information in the following reference: https://www.w3resource.com/javascript-exercises/javascript-function-exercise-3.php

该函数基于以下文档的逻辑,更多信息请参见以下参考:https: //www.w3resource.com/javascript-exercises/javascript-function-exercise-3.php

回答by rustam.crunch

Just to give an option for next who'll search it

只是为了给下一个搜索它的人提供一个选项

const arr = ['a', 'b', 'c']
const combinations = ([head, ...tail]) => tail.length > 0 ? [...tail.map(tailValue => [head, tailValue]), ...combinations(tail)] : []
console.log(combinations(arr)) //[ [ 'a', 'b' ], [ 'a', 'c' ], [ 'b', 'c' ] ]