The Problem with Math.random() Alone

JavaScript’s built-in Math.random() method is handy but limited: it produces a single floating-point number between 0 and 1. To get a series of unique random numbers, you need a custom solution, because this method alone does not track or prevent duplicates.

The Set object solves this elegantly. Since a set only holds unique values, you can use it as a filter: keep generating random integers and inserting them into the set until it reaches your desired size. Duplicates are discarded automatically. This approach requires three steps:

  1. Create a Set.
  2. Decide how many numbers you need, and the upper bound of the range.
  3. Generate random integers and add them to the set until it's full.

The code below shows a basic implementation. The function returns an array (instead of a set) because arrays are more convenient to work with in most scenarios.

function generateRandomNumbers(count, min, max) {
  // 1: Create a `Set` object
  let uniqueNumbers = new Set();
  while (uniqueNumbers.size < count) {
    // 2: Generate each random number
    uniqueNumbers.add(Math.floor(Math.random() * (max - min + 1)) + min);
  }
  // 3: Immediately insert them numbers into the Set...
  return Array.from(uniqueNumbers);
}
// ...set how many numbers to generate from a given range
console.log(generateRandomNumbers(5, 5, 10));

One caveat applies: the count of numbers you request must be less than or equal to the size of the range (max + 1). If you ask for more numbers than the range can provide, the loop will never terminate. You can guard against this with an if check as shown here:

function generateRandomNumbers(count, min, max) {
  // if statement checks that `count` is less than `max + 1`
  if (count > max + 1) {
    return "count cannot be greater than the upper limit of range";
  } else {
    let uniqueNumbers = new Set();
    while (uniqueNumbers.size < count) {
      uniqueNumbers.add(Math.floor(Math.random() * (max - min + 1)) + min);
    }
    return Array.from(uniqueNumbers);
  }
}
console.log(generateRandomNumbers(5, 5, 10));

Turning Random Numbers into Array Indexes

Producing random numbers is only half the job — applying them is where the real value lies. Random array indexes enable practical features such as shuffling a playlist, sampling a dataset at random, or, as one developer did, shuffling tiles in a memory game.

Rather than outputting raw integers, you can use the random numbers to pick elements from a source array. Start with an array of letters:

const englishAlphabets = [
  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 
  'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
];

// rest of code

Then map over the set of random numbers and use each number as an index into the letters array:

const englishAlphabets = [
  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 
  'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
];

// generateRandomNumbers()

const randomAlphabets = randomIndexes.map((index) => englishAlphabets[index]);

In this approach, generateRandomNumbers() supplies the random indices, while a mapping variable consumes them to produce the final selection of letters:

const englishAlphabets = [
  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 
  'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
];

// generateRandomNumbers()

const randomIndexes = generateRandomNumbers(5, 0, 25);
const randomAlphabets = randomIndexes.map((index) => englishAlphabets[index]);

The result is logged to the console exactly as before:

const englishAlphabets = [
  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 
  'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
];

// generateRandomNumbers()

const randomIndexes = generateRandomNumbers(5, 0, 25);
const randomAlphabets = randomIndexes.map((index) => englishAlphabets[index]);
console.log(randomAlphabets);

Putting the function definition back in gives the complete example:

const englishAlphabets = [
  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 
  'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
];
function generateRandomNumbers(count, min, max) {
  if (count > max + 1) {
    return "count cannot be greater than the upper limit of range";
  } else {
    let uniqueNumbers = new Set();
    while (uniqueNumbers.size < count) {
      uniqueNumbers.add(Math.floor(Math.random() * (max - min + 1)) + min);
    }
    return Array.from(uniqueNumbers);
  }
}
const randomIndexes = generateRandomNumbers(5, 0, 25);
const randomAlphabets = randomIndexes.map((index) => englishAlphabets[index]);
console.log(randomAlphabets);

If you want to shuffle the whole array instead of picking a subset, pass englishAlphabets.length as the count argument to generateRandomNumbers:

generateRandomNumbers(englishAlphabets.length, 0, 25);

This pattern is not limited to letters — any source array can be randomly re-ordered or filtered using the same set-based index generation.