Choose a Random Option based on a Range

Avatar of Chris Coyier
Chris Coyier on

I have three options and I need to randomly pick one (in JavaScript). But not equally random. I want there to be a 10% chance of the first option, 20% chance of the second one, and 70% chance of the third.

Here are some ways to do that.

Imagine the data is like this:

var choices = [
  [10, "apples"],
  [20, "oranges"],
  [70, "bananas"] 
];

Plus we’ve set scoped variables for stuff like iterators and random numbers and stuff. Let’s just call this conceptual code, not production code.

Test between a min and max

The first place my mind went was to generate a random number between 1-100 and then test if that number was between…

  • 1-10 for apples
  • 11-30 for oranges
  • 31-100 for bananas

It’s a bit verbose to get those min and max values. You run one loop to iterate through all the choices. The minimum value is zero for the first option, and the sum of all the previous options for all the rest. The maximum value is the sum of the current and all previous options combined.

On each iteration of the loop, you test to see if the random number is within the range that was just generated. If so, set the final choice and break. If not, do another iteration.

function pickChoice() {

  rand = Math.floor(Math.random() * 100);
  choice = -1;
  
  for (i = 0; i < choices.length; i++) {

    // set up min
    if (i === 0) {
      min = 0;
    } else {
      min = 0;
      // add up all the values so far
      for (i2 = 0; i2 < i; i2++) {
        min += choices[i2][0];
      }
      // one higher
      min++;
    }

    // set up max
    if (i === 0) {
      max = choices[i][0];
    } else {
      max = 0;
      // add up all the values so far
      for (i2 = 0; i2 < i + 1; i2++) {
        max += choices[i2][0];
      }
    }

    if (rand >= min && rand <= max) {
      choice = i;
      break;
    }

  }
  
  // If the choice is still -1 here, something went wrong...
  
};

See the Pen % chances of Array items by Chris Coyier (@chriscoyier) on CodePen.

Test if above minimum

David DeSandro had a more clever idea (of course). Give each of the options a minimum value right off the bat. The minimum for each being the sum of all previous options. So the data becomes:

  • 0 for apples
  • 10 for oranges
  • 30 for bananas

Then generate a random number, iterate through the choices, and if the random number is higher than the minimum, set the pick to that. The pick might be set a number of times in this loop, but it will ultimately select the correct option.

function pickChoice() {
  var rand = Math.random() * 100, pick, choice, i;
  // go through choices, update pick if rand is above bottom percent threshold
  for ( i = 0, len = choices.length; i < len; i++ ) {
    choice = choices[i];
    if ( rand > choice.percent ) {
      pick = choice;
    }
  }
  return pick;
}

David also set the data from the values in the HTML for his fork.

See the Pen % chances of Array items by David DeSandro (@desandro) on CodePen.

Build a new Array of all possibilities

George Papadakis made a quick demo with yet another way. Build a new array where each choice is represented a proportionally correct number of times. So with our data, it could be simplified to

  • 1 item in the new array is apples
  • 2 items in the new array are oranges
  • 7 items in the new array are bananas

Then when you generate a random number to choose one, you just pick that right out of the new array, like choices[rand].

// quick'n'dirty
NodeList.prototype.map = Array.prototype.map;

var choices = [], 
  lis = document.querySelectorAll('li')
    .map(function (item) {
      var percent = parseFloat(item.querySelector('span').textContent) / 10,
        label = item.querySelector('strong').textContent;
             
      for (var i = 0; i < percent; i++) {
        choices.push(label)
      }
    });

function pick() {
  var rnd = parseInt(Math.random() * 10);
  document.querySelector('div.choice').innerHTML = choices[rnd];
}

See the Pen % chances of Array items by George Papadakis (@phaistonian) on CodePen.

More?

You could probably remix ideas from all of these into other solutions. Or do this an entirely different way. It’s always fun to see different solutions to the same problem!

My real-world use-case was that I needed to randomize some ads, but base them on percentage-share not just a random split. Any one of these could do that.