Generating Unique Random Numbers

I’m trying to generate 5 unique random numbers between two numbers. The sixth number is a random number between another set of numbers but it doesn’t have to be unique. I tried to ensure the uniqueness of each number by placing the first randomly generated number in an array.

Then each subsequent randomly generated number is checked against the number(s) in this array. Any generated number matching a number in the array will be discarded and a new one is generated until one that doesn’t match any in the array is is found. My code doesn’t work because occasionally it produces duplicates. Please see my code here https://jsfiddle.net/jQing/sLrcteby/15/ and help point out why it is not working.

I think that I’m going to improve some things while working out what’s going on.

First, we can use a separate function to add a leading zero.

   function twoDigits(num) {
     if (num < 10) {
       return "0" + String(num);
     }
     return String(num);
   }
   for (let i = 0; i < ranNum.length; i++) {
     if (i < ranNum.length - 1) {
       firstFive += twoDigits(ranNum[i]) + "&nbsp;" + "&nbsp;" + "&nbsp;" + "&nbsp;" + "&nbsp;" + "&nbsp;";
     } else {
       pb = twoDigits(ranNum[i]);
     }
     fiveNum.innerHTML = firstFive;
     lastNum.innerHTML = pb;
   }

Then, we can use padStart to simplify the twoDigits function.

   function twoDigits(num) {
     return String(num).padStart(2, "0");
   }

Next, the contents of the loop should be put into an array, so that we can join it together with the desired separators.

   const numbers = [];
   for (let i = 0; i < ranNum.length; i++) {
     numbers.push(twoDigits(ranNum[i]));
   }
   fiveNum.innerHTML = numbers.join("&nbsp;" + "&nbsp;" + "&nbsp;" + "&nbsp;" + "&nbsp;" + "&nbsp;");

The lastNum part doesn’t seem to be needed anymore, so we can remove that. I’ll rename fiveNums to be generatedNumbers too.

      <div style="background-color:black">
        <span id="generatedNumbers" style="margin-left: 10%; color:blue;"></span>
      </div>
 // var fiveNum = document.getElementById('fiveNums');
var generatedNumbers = document.getElementById('generatedNumbers');
  // var lastNum = document.getElementById('lastNum');
...
   generatedNumbers.innerHTML = numbers.join("&nbsp;" + "&nbsp;" + "&nbsp;" + "&nbsp;" + "&nbsp;" + "&nbsp;");

The HTML code is full of inline styles. Those really need to be moved out to the CSS section instead.

.container {
  margin-left: 20%;
  width: 500px;
  font-weight: bold;
  font-size: 30px;
}
button {
  margin-left: 36%;
}
label {
  margin-left: 20%;
}
.numberArea {
  background-color: black;
}
#generatedNumbers {
  margin-left: 10%;
  color: blue;
}
<div class="container">
  <button id="generate">Generate Numbers</button>
  <br><br>
  <div>
    <label>Generate Numbers</label>
    <div class="numberArea">
      <span id="generatedNumbers"></span>
    </div>
  </div>
</div>

I don’t like the double break. <br> tags really should only be used for separating addresses and doing poetry. Better techniques exist outside of those two situations.

The break can be removed, and replaced with a bottom margin on the button instead,

button {
  margin-left: 36%;
  margin-bottom: 3em;
}
<div class="container">
  <button id="generate">Generate Numbers</button>
  <div>

There are a lot of margin-left declarations, designed to center things.
I’m going to remove all of those and attempt to simplify things, by using some classes to center the content.

<div class="container center-block">
  <button id="generate" class="center-block">Generate Numbers</button>
  <div>
    <h1 class="center-text">Generate Numbers</h1>
    <div class="numberArea center-text">
      <span id="generatedNumbers"></span>
    </div>
  </div>
</div>
.container {
  width: 500px;
  padding: 0.5em;
}
h1 {
  width: 100%;
  margin-top: 1em;
  margin-bottom: 0em;
}
.numberArea {
  height: 1em;
  background-color: black;
  color: blue;
  font-size: 30px;
  font-weight: bold;
}
.center-block {
  display: block;
  margin: 0 auto;
}
.center-text {
  text-align: center;
}

That’s a bit better.

https://jsfiddle.net/bgcht2fx/1/

I’ll carry on next now with improving the scripting code.

The code that generates was updated to the folliowing:

   const numbers = [];
   for (let i = 0; i < ranNum.length; i++) {
     numbers.push(twoDigits(ranNum[i]));
   }
   generatedNumbers.innerHTML = numbers.join("&nbsp;" + "&nbsp;" + "&nbsp;" + "&nbsp;" + "&nbsp;" + "&nbsp;");

That for loop isn’t needed, as we can simply map the random array to the numbers we want.

   // const numbers = [];
   // for (let i = 0; i < ranNum.length; i++) {
   //   numbers.push(twoDigits(ranNum[i]));
   // }
   const numbers = ranNum.map(twoDigits);
   generatedNumbers.innerHTML = numbers.join("&nbsp;" + "&nbsp;" + "&nbsp;" + "&nbsp;" + "&nbsp;" + "&nbsp;");

With the GenerateNumbers function, an easy way to deal with duplicates is to put the numbers into a set. That way duplicates are automatically dealt with, and the size of the set tell you when you are ready to finish.

I’ve also replaced the uppercase first character of the function name, as uppercase has a special meaning that it’s a constructor used with the new keyword.

   const ranNum = generateNumbers(6);
...
 function generateNumbers(amount) {
    const numbers = new Set();
    while (numbers.size < amount) {
       numbers.add(Math.floor(Math.random() * 75) + 1);
    }
    return Array.from(numbers);
 }

And as a last thing, the repeated spaces can instead use the string repeat method to repeat them.

  // generatedNumbers.innerHTML = numbers.join("&nbsp;" + "&nbsp;" + "&nbsp;" + "&nbsp;" + "&nbsp;" + "&nbsp;");
  generatedNumbers.innerHTML = numbers.join("&nbsp;".repeat(6));

Moving code out to separate functions has let me reduce the event handler function to just the following:

btnGen.addEventListener('click', function() {
  const ranNum = generateNumbers(6);
  ranNum.sort(numericSort);

  const numbers = ranNum.map(twoDigits);
  generatedNumbers.innerHTML = numbers.join("&nbsp;".repeat(6));
});

That seems like a good place to stop for now. https://jsfiddle.net/bgcht2fx/3/

I think the sixth random number needs to be appended after the first 5 random numbers are sorted.

1 Like

Fair enough, I was losing the woods for the trees.

We can use a separate function to get a random number:

function rand(limit) {
  return Math.floor(Math.random() * limit) + 1;
}

and add on a bonus number at the end of 5 numbers.

  const ranNum = generateNumbers(5);
  ranNum.sort(numericSort);
  ranNum.push(rand(42));
...
  while (numbers.size < amount) {
    numbers.add(rand(75));
  }

https://jsfiddle.net/8wemf4xs/

Hi Paul,
Thanks for all your help. Your solution is a lot cleaner than mine. I think that I almost had it working correctly. I now realize I missed a crucial step in my original work and that was, before adding the last number to the array, I should have sorted the array after the first five numbers were added to it.

1 Like