I am selecting randomly 20 bikes from a larger array of bikes, and then creating a new array with those selected bikes. I would like to make sure that at least 2 of the randomly selected bikes in that new array are the color red. More than 2 can be red, but at a minimum 2 should be. How is this achieved while making it fair to all of the randomly selected bikes?
Some example code:
const bikes = [
{ type: 'bmx', color: 'red' },
{ type: 'mountain', color: 'blue' },
{ type: 'bmx', color: 'yellow' },
{ type: 'mountain', color: 'black' },
{ type: 'bmx', color: 'red' },
{ type: 'bmx', color: 'purple' }
...more bikes
]
const getRandomBikes = () => {
const randomBikes = []
//random select 20 bikes
for(let i = 0; i < 20; i++) {
randomBikes.push(bikes[Math.floor(Math.random() * bikes.length)])
}
return randomBikes
}
getRandomBikes()
So pretty simple to return a random array. But now how would I make sure that the random bike array contained at least two bikes with color: 'red' without effecting the probability of every bike being selected? Presumably I would be running getRandomBikes() until the returned array satisfied the requirements, but say my array of bikes is 10000 long, that may take forever before the requirement is met. How do I ensure at least two red bikes end up in the randomly selected bike array?
While iterating, you might count up the number of required bikes of a specific color remaining, and if equal to the number of items that remain to be chosen, pick only from the bikes with the right color.
const bikes = [
{ type: 'bmx', color: 'red' },
{ type: 'mountain', color: 'blue' },
{ type: 'bmx', color: 'yellow' },
{ type: 'mountain', color: 'black' },
{ type: 'bmx', color: 'red' },
{ type: 'bmx', color: 'purple' }
];
const getRandomBikes = (requiredRed, totalPicks) => {
const chosenBikes = [];
const reds = bikes.filter(({ color }) => color === 'red');
for(let i = 0; i < totalPicks; i++) {
const redsSoFar = chosenBikes.reduce((a, b) => a + (b.color === 'red'), 0);
const picksRemaining = totalPicks - i;
const mustChooseRed = requiredRed - redsSoFar === picksRemaining;
chosenBikes.push(
mustChooseRed
? reds[Math.floor(Math.random() * reds.length)]
: bikes[Math.floor(Math.random() * bikes.length)]
);
}
return chosenBikes;
}
console.log(getRandomBikes(2, 3));
Or with less complexity
const bikes = [
{ type: 'bmx', color: 'red' },
{ type: 'mountain', color: 'blue' },
{ type: 'bmx', color: 'yellow' },
{ type: 'mountain', color: 'black' },
{ type: 'bmx', color: 'red' },
{ type: 'bmx', color: 'purple' }
];
const getRandomBikes = (requiredRed, totalPicks) => {
const chosenBikes = [];
const reds = bikes.filter(({ color }) => color === 'red');
let redsSoFar = 0;
for(let i = 0; i < totalPicks; i++) {
const picksRemaining = totalPicks - i;
const mustChooseRed = requiredRed - redsSoFar === picksRemaining;
const chosenBike = mustChooseRed
? reds[Math.floor(Math.random() * reds.length)]
: bikes[Math.floor(Math.random() * bikes.length)]
if (chosenBike.color === 'red') redsSoFar++;
chosenBikes.push(chosenBike);
}
return chosenBikes;
}
console.log(getRandomBikes(2, 3));