【问题标题】:How to get all combination of the values from a 2D array's rows with JavaScript?如何使用 JavaScript 从二维数组的行中获取所有值的组合?
【发布时间】:2019-05-24 02:29:56
【问题描述】:

我正在使用的二维数组的每一行都有不同的长度,例如:

var a = [2, 5, -12, 9];
var b = [54.0, 0.3];
var c = ["tree", "sun", "pool"]
var all = [a, b, c]

二维数组中的任何行有时都可能为零。上面的数组只是一个示例。

我想做的是从每个人的每一行中获取一个值,用这些值做一些事情,然后得到另一个值的组合,等等。

例子:

//IF ALL ROWS HAVE CONTENT
var values = [all[0][0], all[1][0], all[2][0]];
//do something with it
values = [all[0][0], all[1][0], all[2][1]];
//do something with it
......
values = [all[0][3], all[1][1], all[2][2]];
//do something with it

//IF FIRST AND THRID ROWS HAVE CONTENT, THE SAMPLE OUTPUT
var values = [all[0][0], all[2][0]];
values = [all[0][0], all[2][1]];
......
values = [all[0][3], all[2][2]];

//IF ONLY SECOND ROWS HAVE CONTENT, THE SAMPLE OUTPUT
var values = [all[1][0]];
values = [all[1][1]];

这是我对代码逻辑流程的看法

//count how many rows are not empty
var arrayCount = 0;
for(var i=0; i < all.length; i++){
   if(all[i].length !== 0){
      arrayCount++;
  }
}
//store the combination of values each time
var values = [];
//reference for rows
var x; var y;
//differentiate the looping based on the number of unempty rows
switch(arrayCount){
   //one unempty row
   case 1:
      //figure out which one is not empty and set a's pointer to it
      for(var q = 0; q < x.length; q++){
         values.push(x[q]);
         //do something with it
         values.splice(1, 0);
      }
      break;
   case 2:
      //figure out which one are not empty and set a and b's pointer to them (don't know how, maybe using two loops for each row?)
      for(var q = 0; q < x.length; q++){
         values.push(x[q]);
         for(var p = 0; p < y.length; p++){
            values.push(y[p]);
            //do something with it
            values.splice(1, 1);
         }
         values.splice(1, 0);
      }
      break;
   case 3:
      //set pointers to all the rows
      for(var q = 0; q < x.length; q++){
         values.push(x[q]);
         for(var p = 0; p < y.length; p++){
            values.push(y[p]);
            for(var r = 0; r < z.length; r++){
               values.push(z[r]);
               //do something with it
               values.splice(1, 2);
            }
            values.splice(1, 1);
         }
         values.splice(1, 0);
      }
      break;
}

恐怕整个代码太长了,并且在开关中有一些重复的代码。可以简化吗?

我确实看到post 提出了同样的问题,我尝试了它的答案。不幸的是,我正在编码的平台(Fandom)不支持这个生成器功能。我问,它只支持 Javascript 到 ES3 或 ES4。

感谢您查看这个问题!

【问题讨论】:

  • 这并不完全符合您的要求,但我最近需要生成一组值的所有可能排列,并使用this recursive function 完成了它。我认为它可能会帮助您想出完成任务的方法。
  • 我在问题中添加了一个 ES4 标签,Fandom as in fandom.com 而你使用的是dev.fandom.com/wiki/Fandom_Developers_Wiki?还是其他文档?如果您澄清您正在使用的平台是什么,我将添加一个 Fandom 标签以帮助您获得正确的答案?
  • @rayhatfield 谢谢你的链接,我去看看
  • @JasonB 抱歉回复晚了,我所说的 Fandom 只是 fandom.com。在我发布这个问题之前,我没有找到与 Fandom 相关的标签。
  • 还有另一个 stackoverflow 答案可以解决这个确切的问题。我通过 babel 的在线 repl 运行他们的代码,得到了丑陋但可操作的代码。我根据您的样本数据对其进行了测试,并得到了预期的结果。我没有发布它,因为它看起来其他人已经在这里回答了,但如果你想查看它,我可以挖掘一个链接。

标签: javascript arrays loops ecmascript-4


【解决方案1】:

这是一个处理空数组并且不使用生成器函数的解决方案。

var combinations = all.reduce(function (previous, current) {

    if (current.length === 0)
        return previous;

    if (previous.length === 0)
        return current;

    const accumulate = current.map(function (x){

        return previous.map(function(y) {

            // Make one array if the accumulated result is an array
            if (y.length > 0)
                return y.concat(x);

            return [x, y];
        });
    });

    // Flatten combinations
    return accumulate.reduce( function (acc, x) {
        return acc.concat(x);
    });
});

【讨论】:

  • 感谢您提供简洁的代码,并为迟到的回复感到抱歉,因为我花了一些时间尝试阅读您的代码和测试。我在测试过程中确实发现了一些问题。你介意看看它并修复你的代码吗? Test result here
  • @AnranQin 我已经更新了解决方案以涵盖您测试的边缘情况。我还添加了一个条件来确保每个组合都没有嵌套数组
  • 很抱歉再次打扰您,我在您的代码中发现了另一个错误。如果第一行的值是一个字符串,在它与其他值连接后,它将作为一个字符串而不是一个数组返回。 Sample output here我花了一些时间试图了解发生了什么但没有任何线索。你介意再看一遍吗?谢谢
【解决方案2】:

您可以采用递归方法,通过获取数组数组并迭代每个内部数组并移交收集的项目数组,直到没有更多可用数组为止。

function getCartesian(array) {
    function iter(temp)  {
        var i = temp.length, j;
        if (i >= array.length) {
            return result.push(temp);
        }
        for (j = 0; j < array[i].length; j++) {
            iter(temp.concat(array[i][j]));
        }
    }

    var result = [];
    iter([]);
    return result;
}

console.log(getCartesian([[2, 5, -12, 9], [54.0, 0.3], ["tree", "sun", "pool"]]).map(a => a.join(' ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }

一个功能

function getCartesian(array) {
    var i, j,
        first = array.shift(),
        temp = [],
        result = [];

    if (!first) return;
    if (!array.length) return first;
    temp = getCartesian(array);

    for (i = 0; i < first.length; i++)
        for (j = 0; j < temp.length; j++)
            result.push([first[i]].concat(temp[j]));

    return result;
}

console.log(getCartesian([[2, 5, -12, 9], [54.0, 0.3], ["tree", "sun", "pool"]]).map(a => a.join(' ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

  • 感谢您展示了一个用递归解决问题的好方法!但是,在一些示例数组中测试后,我发现您的代码存在一些问题:如果 2D 数组中的任何行为空,则该函数将仅返回一个空数组。 Test Result here
  • 你期望空数组的结果是什么?
【解决方案3】:

我通过babel's online replthis answer 提出了一个类似的问题,得到了丑陋但可操作的代码,似乎可以满足您的需求。

警告:这引用了Symbol.iterator,它在 ES4 中可能不可用。我没有研究过。如果您想修改 babel 设置以实现兼容性,上面的 babel 链接包括原始解决方案和此转译。

我最初没有发布这个,因为看起来人们已经使用相同的算法回答了你的问题,而没有编译的丑陋,但是既然你问了......

这是针对您的示例输入运行的结果:

const a = [2, 5, -12, 9];
const b = [54.0, 0.3];
const c = ["tree", "sun", "pool"];
const all = [a, b, c];

function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }

function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); }

function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }

function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }

var makeCartesian = function makeCartesian() {
  var t = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  return function (a) {
    for (var _len = arguments.length, more = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
      more[_key - 1] = arguments[_key];
    }

    return a === undefined ? [t] : a.flatMap(function (x) {
      return makeCartesian([].concat(_toConsumableArray(t), [x])).apply(void 0, more);
    });
  };
};

var cartesian = makeCartesian();
console.log(cartesian.apply(void 0, _toConsumableArray(all)));

【讨论】:

  • 感谢您在这里分享,我会花些时间看看它!
  • 嗨,我用几个示例数组尝试了你的代码,如果输入的二维数组至少有一个空行,它将返回一个空数组。顺便说一句,我为我的误导性示例道歉,只是发现我没有列出所有可能的输入并且想要正确输出,已经编辑了它们。您介意再看一下这个问题吗?
  • 您是否考虑过自己努力解决这个问题?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-05
  • 2019-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多