DEV Community

Sagdi Formanov
Sagdi Formanov

Posted on

Generate unique (non-repeating) random numbers

Backstory: when I was doing one of my side projects, one of the tasks was to display photos from Unspash API in multiple cards. Goal was to pick photos randomly without repeating them. I used good old Math.random() to randomly pick photos from API's response. But the problem was that often few numbers were repeating, thus there were the same pictures in different cards.

Let's say you want to generate 5 random unique numbers from 1 to 10. Big chances that you'll get at least one repeated number.

Solution for this task is to replace each picked (random) number in array with another unused one.

In the code this would be something like this:

function randomUniqueNum(range, outputCount) {

  let arr = []
  for (let i = 1; i <= range; i++) {
    arr.push(i)
  }

  let result = [];

  for (let i = 1; i <= outputCount; i++) {
    const random = Math.floor(Math.random() * (range - i));
    result.push(arr[random]);
    arr[random] = arr[range - i];
  }

  return result;
}
Enter fullscreen mode Exit fullscreen mode

Let's look line by line.
Function takes range, and output count.
For instance randomUniqueNum(10, 5)
First we generate array from 1 to 10

 let arr = []
  for (let i = 1; i <= 10; i++) {
    arr.push(i)
  }
Enter fullscreen mode Exit fullscreen mode

arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Then we use another loop to pick numbers from this array.
Iterations in this loop equals to output count, in this case to 5.
Then we generate random number:

const random = Math.floor(Math.random() * (range - i))
Enter fullscreen mode Exit fullscreen mode

Each iteration we decreasing range by 1.
Then using this random number as index in arr we push it to the result array:

result.push(arr[random])
Enter fullscreen mode Exit fullscreen mode

After that we replace 'used' number in arr with the one from the end of the same array:

arr[random] = arr[range - i]
Enter fullscreen mode Exit fullscreen mode

Even though in the next iteration Math.random() will give us the same number, we'll get different result because we replaced it with the number from the end.

Since we decrease range in each iteration: range - i so numbers from upper end of array will not be picked.
At the end we just return array of unique random numbers.

I hope this would help you in any way.

Top comments (3)

Collapse
 
shane325 profile image
Shane Barry • Edited

Nice. Here's another implementation using a Set()

const randomUnique = (range, count) => {
    let nums = new Set();
    while (nums.size < count) {
        nums.add(Math.floor(Math.random() * (range - 1 + 1) + 1));
    }
    return [...nums];
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
k16e profile image
Kabolobari Benakole

How's this solution work, though. And it's rather difficult to read. Would you break it down? I mean it looks handy...but

Collapse
 
sagdish profile image
Sagdi Formanov • Edited

This is so cool! Gotta love new JS features