Как получить всю комбинацию значений из строк 2D-массива с помощью JavaScript?

2D-массив, с которым я работаю, имеет разную длину для каждой строки, что-то вроде:

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

Любая строка в 2D-массиве иногда может быть нулевой. Приведенный выше массив является лишь примером.

Я хочу получить по одному значению из каждой строки, сделать что-то с этими значениями, затем получить другую комбинацию значений и т. Д.

Пример:

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

Я боюсь, что весь код слишком длинный и имеет несколько дублирующих кодов в коммутаторе. Возможно ли это упростить?

Я видел сообщение с тем же вопросом и пытался ответить на него. К сожалению, платформа, на которой я пишу код (Fandom), не поддерживает эту функцию генератора. Я спросил, это только поддержка Javascript до ES3 или ES4.

Спасибо, что взглянули на этот вопрос!

3 ответа

Решение

Вот решение, которое обрабатывает пустые массивы и не использует функции генератора.

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

Я запустил этот ответ на похожий вопрос через онлайн-реплан Babel и получил некрасивый, но рабочий код, который, кажется, делает то, что вы хотите.

Будьте внимательны 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)));

Вы могли бы использовать рекурсивный подход, взяв массив массивов, итерируя каждый внутренний массив и передавая массив собранных элементов, пока больше не будет доступных массивов.

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

Другие вопросы по тегам