2

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 ]
2
  • Your code works - exactly what sort of improvement are you looking for? (might more more suited to codereview than here) Commented Jul 15, 2018 at 5:57
  • Hi @CertainPerformance , Is there already a function/method or a combination of functions/methods that achieves this? Also, codereview, is that a section on Stack? Commented Jul 15, 2018 at 6:04

4 Answers 4

2

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

Sign up to request clarification or add additional context in comments.

4 Comments

This loop is not "straightforward", rather it is full of twisty passages, all alike
@Tibrogargan, I have removed the word straightforward and added more explanation. Do you think this is clear enough?
Yes, I believe so. This is a delightful piece of code, but may be difficult for a novice to decipher without the explanation.
Thanks @trincot! It maintains all functionality, allowing for "finish" to go above 9, and also specify the quantity. Clean and easy to read. Also intruced me to new methods and the idea that code may be more efficient without recursion. :)
2

If you are willing to do a little math, there is a pretty quick easy way to do this in general terms. The basic insight is that a number like 768 can be broken down to various log10 components taken modulo 10. For example Math.floor(768/100) % 10 gets you the third digit. Math.floor(768/10) % 10 get you the second. To get the length of the inner arrays you need you can take Math.floor(Math.log10(largestNumber + 1)). So for 1000 this will be 4, for 999 it will be 3, etc. The only annoying part of this arrangement it that arrays are build left to right but numbers are build right to left. Thats why we are working with length - index in the inner arrays.

You can put this together with Array.from to make a succinct function that avoids a lot of string parsing and if/else clauses:

function genArray(start, finish) {
  return Array.from({length: finish - start + 1}, (_, i) => {
    let ind = i + start
    let innerLength = Math.floor(Math.log10(finish + 1))
    return Array.from({length: innerLength + 1}, (_, i) => Math.floor(ind / (10 ** (innerLength - i))) % 10)
  })
}

let a = genArray(0, 20)
console.log(a.join(' · '))

a = genArray(1020, 1040)
console.log(a.join(' · '))

Also, it's not clear how large your arrays will be, but if you are working with large sets of numbers, it can be a little more memory efficient to make a generator so you only produce the inner arrays as needed. It's not right solution for everything, but since it's almost the same code, I thought I'd mention it:

function* genArray(start, finish) {
  let innerLength = Math.floor(Math.log10(finish + 1))
  while (start <= finish) {
    yield Array.from({length: innerLength + 1}, (_, i) => Math.floor(start / (10 ** (innerLength - i))) % 10)
    start++
  }
}
let a = genArray(101, 105)
console.log(a.next().value)

let b = genArray(20, 30)
console.log([...b].join(' · '))

Comments

1

This is a possible solution:

function genArrays(start, end){
    let max_len = String(end).length;
    let arr = [];
    let cur_num = start;
    let arr_num;

    for (let i = start; i <= end; i++){
        str_num = String(cur_num).padStart(max_len, '0').split('').map(x => parseInt(x));

        arr.push(str_num);

        cur_num++;
    }
    
    return arr;
}

console.log(genArrays(0, 1000));
console.log(genArrays(102, 1043));

The core is here: str_num = String(cur_num).padStart(max_len, '0');. A counter is firstly stringed and then it is applied a padding on the left in order to reach the length of the stringed end.

1 Comment

Doesn't account for all the arguments of the original
0

I don't speak a perfect english, and I hope to understand your question.

From the example you provided, it seems you just need a 2 level array, the first one containing n arrays of incremental numbers.

Instead of using a recursive function, that use lot of memory, you can try to use a normal for cycle, and a split on every number, padded with 0s to get the n length.. Don't know if it could work for you.

0002.split create an array [0,0,0,2].. and then push it on the main array

Should be the fastest way

//s start, e end, n array size
f=new Array();
a=new Array();
for (i=s; i<e; i++) {
 t=pad(i,n);
 a=i.toString().split('');
 f[f.length]=a;
}

function pad(s,n) { 
   while (s.length < n) 
      s = '0' + s; 
   return s; 
};

Cheers Daniele

1 Comment

Your pad could be replaced by String.padStart. Perhaps you could declare your variables.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.