0

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!

Anran Qin
  • 7
  • 3
  • This doesn't do quite what you're after, but I recently needed to generate all possible permutations of a set of values and did it with [this recursive function](https://gist.github.com/rayhatfield/9f55ea66bdc27b5071e3b70b9bc870c3). I thought it might help you think of a way to accomplish your task. – ray May 24 '19 at 02:35
  • I added an ES4 tag to the question, Fandom as in https://www.fandom.com/ and you are using https://dev.fandom.com/wiki/Fandom_Developers_Wiki? Or something else for documentation? I'll add a Fandom tag to help you get proper answers if you clarify what the platform is that you are using? – JasonB May 24 '19 at 04:03
  • @rayhatfield Thanks for your link, I'll take a look at it – Anran Qin May 24 '19 at 16:32
  • @JasonB Sorry for the late reply, the Fandom I'm talking about is just fandom.com. I didn't find a tag relate to the Fandom before I post this question though. – Anran Qin May 24 '19 at 16:35
  • There’s another stackoverflow answer that solves this exact problem. I ran their code through babel’s online repl and got ugly but operational code. I tested it against your sample data and got the expected result. I didn’t post it because it looked like other people had already answered here, but if you want to check it out I can dig up a link. – ray May 24 '19 at 17:12
  • @rayhatfield Thank you for mention about this, and please share the link if you can find it. It's always a good idea to take a look at different solution! – Anran Qin May 24 '19 at 23:36
  • I posted the babel translation of [this answer](https://stackoverflow.com/a/55711373/636077) into an answer to this question [here](https://stackoverflow.com/a/56300723/636077). – ray May 25 '19 at 00:17

3 Answers3

0

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);
    });
});
  • 1
    Thanks for you neat codes, and sorry for the late reply because I spend some times on trying to read you codes and testing. I did find some problem during the testing. Would you mind to take a look at it and fix your code a bit? [Test result here](https://imgur.com/a/HrIKYnb) – Anran Qin May 25 '19 at 01:19
  • @AnranQin I've updated the solution to cover for the edge cases you tested. I've also added a condition to make sure each combination has no nested arrays –  May 25 '19 at 07:14
  • Sorry for disturb you again, I find another bug in your code. If the value in first row is a string, after it concat with other values, it will return as a string rather than an array. [Sample output here](https://imgur.com/a/PFu0t1Q) I spend some time trying to understand what's happened but got no clue. Would you mind to take a look at it again? Thx – Anran Qin May 27 '19 at 14:18
0

You could take a recursive approach by taking the array of arrays and iterate each inner array and hand over an array of the collected items until no more arrays are available.

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

With one function

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; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • Thanks for showing a nice approach to the question with the recursive! However I find a little problem with your code after test in some sample arraies: If any row in the 2D array is empty, the function will only return an empty array. [Test Result here](https://imgur.com/a/6Xairjd) – Anran Qin May 25 '19 at 00:15
  • what result do you expect for empty arrays? – Nina Scholz May 25 '19 at 08:03
0

I ran this answer to a similar question through babel's online repl and got ugly but operational code that appears to do what you want.

Caveat: This references Symbol.iterator, which may not be available in ES4. I haven't researched it. The babel link above includes the original solution and this transpilation if you want to tinker with the babel settings for compatibility.

I didn't post this initially because it looked like people had already answered your question using the same algorithm without the ugliness of transpiling, but since you asked...

Here's the result running against your sample input:

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)));
ray
  • 26,557
  • 5
  • 28
  • 27
  • Thanks for sharing it here, I'll take some times to look at it! – Anran Qin May 25 '19 at 00:27
  • Hi ray, I tried your code with several sample array, it will return an empty array if the input 2D array have at least one empty row. By the way I apology for my misleading example, just find out I didn't list all possible input and wanted output correctly, already edit them. Would you mind to take a look at the question again? – Anran Qin May 25 '19 at 01:36
  • Have you considered making an effort of your own to figure it out? – ray May 25 '19 at 02:07