Search code examples
javascriptarraysarrayofarrays

Javascript - Is there a more efficient way to create an array of arrays? - Examples provided


Question: Is there a more efficient way of creating an array of arrays of incrementing numbers?

I've created a function to produce an array of arrays of incrementing numbers, which took far longer than expected, and I'm sure there is a more efficient way to achieve this (I'm new to JS).

Note for the genArray function of both example 1 and 2: argu1 declares the start of the number range (e.g. 0 = start from 0), argu2 declares the end of the number range (e.g. 9 = end at 9), argu3 declares how many numbers are needed in each individual array (e.g. 3 = generate 3 numbers in the array), argu4 carries the temp array to generate a single array of numbers, argu5 carries the array of arrays through the function and nested functions.

Example 1: Below is the code purely for creating an array of arrays of incrementing numbers. My question refers to making a more efficient version of this function.

function genArray(start, finish, quantity, array, allArray = []) {
  var collectArray = allArray;

  //Cycle through digits from start to finish, e.g. 0-9
  for (var i = start; i <= finish; i++) {
    var tempArray = [];

    //Collect digits for a single array if not first iteration
    if (array !== undefined) {
      tempArray = tempArray.concat(array);
    };

    //Add digit to single level array
    tempArray.push(i);

    //If not highest level, go higher
    if (quantity > 1) {
      var genArray2 = genArray(start, finish, quantity-1, tempArray, collectArray);
    } 

    //If highest level collect a single array
    else if (quantity == 1) {
      collectArray.push(tempArray);
    }
  }
  return collectArray;
}

//Call function with arguments
//argu1 declares the start of the number range, argu2 declares the end of the number range, argu3 declares how many numbers are needed in each individual array, argu4 carrays the temp array to generate a single array of numbers, argu4 carrys the array of arrays throught the function and nested functions. 
var genArray2 = genArray(0, 9, 3);
console.log(genArray2);

This produces a log like so:

[ [ 0, 0, 0 ],
  [ 0, 0, 1 ],
  [ 0, 0, 2 ],
  [ 0, 0, 3 ],
  [ 0, 0, 4 ],
  [ 0, 0, 5 ],
  [ 0, 0, 6 ],
  [ 0, 0, 7 ],
  [ 0, 0, 8 ],
  [ 0, 0, 9 ],
  [ 0, 1, 0 ],
  [ 0, 1, 1 ],
  [ 0, 1, 2 ],
  [ 0, 1, 3 ],
  [ 0, 1, 4 ],
  [ 0, 1, 5 ],
  [ 0, 1, 6 ],
  [ 0, 1, 7 ],
  [ 0, 1, 8 ],
  [ 0, 1, 9 ],
  [ 0, 2, 0 ],
  [ 0, 2, 1 ],
  [ 0, 2, 2 ],
  [ 0, 2, 3 ],
  [ 0, 2, 4 ],
  [ 0, 2, 5 ],
  [ 0, 2, 6 ],
  [ 0, 2, 7 ],
  [ 0, 2, 8 ],
  [ 0, 2, 9 ],
  [ 0, 3, 0 ],
  [ 0, 3, 1 ],
  [ 0, 3, 2 ],
  [ 0, 3, 3 ],
  [ 0, 3, 4 ],
  .... up to [ 9, 9, 9 ]

Example 2: Below is the code I'm actually using, with the only change being the addition of a check to see if an array produced is ascending and each number is unique, and storing only those that are true in both cases. Providing this for context and in case it's useful to someone:

//Check if ascending
function ascending(x) {
  return x == parseInt(x.toString().split('').sort().join(''));
}

//Check if unique
function unique(x) {
  return x.toString().split('').length == [...new Set(x)].length
}

//Create an array of arrays of ascending and unique numbers    
function genArray(start, finish, quantity, array, allArray = []) {
  var collectArray = allArray;

  //Cycle through digits from start to finish, e.g. 0-9
  for (var i = start; i <= finish; i++) {
    var tempArray = [];

    //Collect digits for a single array if not first iteration
    if (array !== undefined) {
      tempArray = tempArray.concat(array);
    };

    //Add digit to single level array
    tempArray.push(i);

    //If not highest level, go higher
    if (quantity > 1) {
      var genArray2 = genArray(start, finish, quantity-1, tempArray, collectArray);
    } 

    //If highest level collect a single array
    else if (quantity == 1 && ascending(tempArray.join('')) && unique(tempArray.join(''))) {
      collectArray.push(tempArray);
    }
  }
  return collectArray;
}

//Call function with arguments
var genArray2 = genArray(0, 9, 3);
console.log(genArray2);

This produces a log like so:

[ [ 0, 1, 2 ],
  [ 0, 1, 3 ],
  [ 0, 1, 4 ],
  [ 0, 1, 5 ],
  [ 0, 1, 6 ],
  [ 0, 1, 7 ],
  [ 0, 1, 8 ],
  [ 0, 1, 9 ],
  [ 0, 2, 3 ],
  [ 0, 2, 4 ],
  [ 0, 2, 5 ],
  [ 0, 2, 6 ],
  [ 0, 2, 7 ],
  [ 0, 2, 8 ],
  [ 0, 2, 9 ],
  [ 0, 3, 4 ],
  [ 0, 3, 5 ],
  [ 0, 3, 6 ],
  [ 0, 3, 7 ],
  [ 0, 3, 8 ],
  [ 0, 3, 9 ],
  [ 0, 4, 5 ],
  [ 0, 4, 6 ],
  [ 0, 4, 7 ],
  [ 0, 4, 8 ],
  [ 0, 4, 9 ],
  [ 0, 5, 6 ],
  [ 0, 5, 7 ],
  [ 0, 5, 8 ],
  [ 0, 5, 9 ],
  [ 0, 6, 7 ],
  [ 0, 6, 8 ],
  [ 0, 6, 9 ],
  [ 0, 7, 8 ],
  [ 0, 7, 9 ],
  [ 0, 8, 9 ],
  [ 1, 2, 3 ],
  [ 1, 2, 4 ],
  [ 1, 2, 5 ],
  [ 1, 2, 6 ],
  .... up to [ 7, 8, 9 ]

Solution

  • Without recursion you will be able to speed it up. Here is a loop that just uses the previously added subarray to calculate the next. It uses the mechanism one has when adding a 1 to a decimal number: first increment the right most digit. If it goes out of range (in decimal: it becomes 10), then set it back to the lowest digit and increment the digit on its left, ...etc, until the last changed digit remains within range:

    function genArray(start, finish, quantity) {
        const current = Array(quantity).fill(start);
        const result = [];
        for (let i = quantity; i >= 0; null) {
            result.push(current.slice());
            for (i = quantity; i--; null) {
                current[i]++;
                if (current[i] <= finish) break; 
                current[i] = start;
            }
        }
        return result;
    }
    
    console.log(genArray(0, 2, 3));