javascriptarraysloopsecmascript-4

How to get all combination of the values from a 2D array's rows with JavaScript?


the 2D array I'm working with have different length for each row, something like:

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

Any row in the 2D array might be zero sometimes. The array above is just an example.

What I want to do is to get one value from each row everyone, do something with these value, then get another combination of the values, etc.

Example:

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

Here are my thought on the logical flow of the codes

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

I'm afraid the whole code is too long, and having some duplicate codes in the switch. Is that possible to simplify it?

I did saw a post with same question, and I tried its answer. Unfortunely, the platform I'm coding on (Fandom) doesn't support this generator function. I asked, it's only support Javascript upto ES3 or ES4.

Thank you for taking a look at this question!


Solution

  • Here's a solution that handles empty arrays and does not use generator functions.

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