It's because you should be pushing the item into the array as soon as you see that it's not in array
, so that the item that brings the array to its max length is the one that also clears the array
.
The way it is right now, the .push()
that brings the array to its max doesn't clear the array, so on the next call, whatever number is produced will always be included in the array, making your recursion always happen.
function generateRandomNumber(array, maxN) {
let randomN = Math.floor(Math.random() * maxN);
if(!array.includes(randomN)) {
if(array.push(randomN) == maxN) {
console.log('max reached');
array.length = 0;
}
return randomN;
} else {
return generateRandomNumber(array, maxN);
}
}
I also made a proper tail call by returning immediately on the recursive call without it depending on any variable in the current scope. I also took advantage of the fact that .push()
returns the new .length
, so its return value can be used for the comparison.
I'd probably also rearrange it to use a positive condition.
function generateRandomNumber(array, maxN) {
let randomN = Math.floor(Math.random() * maxN);
if(array.includes(randomN)) {
return generateRandomNumber(array, maxN);
}
if(array.push(randomN) == maxN) {
console.log('max reached');
array.length = 0;
}
return randomN;
}
And personally, I think the function should use its own array. I'd get rid of the array
parameter, and put var array = [];
inside the outmost IIFE function.
Since this needs to be reused, you could make a factory function that produces a unique function with its own array and max length.
function makeNumberGenerator(maxN) {
var array = [];
return function generateRandomNumber() {
let randomN = Math.floor(Math.random() * maxN);
if(array.includes(randomN)) {
return generateRandomNumber(array, maxN);
}
if(array.push(randomN) == maxN) {
console.log('max reached');
array.length = 0;
}
return randomN;
}
}
Then to use one of these, you'd first create a number generator for the desired size:
var randomGen5 = makeNumberGenerator(5);
Then use it without params, since it already has the max and the array.
var num = randomGen5(); // 2 (or whatever)
Here's a more efficient algorithm for producing a random number from a range:
function makeNumberGenerator(maxN) {
// Add some code to ensure `maxN` is a positive integer
var array = [];
return function generateRandomNumber() {
if(!array.length) {
array = Array.from(Array(maxN), (_, i) => i);
}
return array.splice(Math.floor(Math.random() * array.length), 1)[0];
}
}