Reputation: 4538
I want to run a function that each time randomly chooses an element from an array that wasn't chosen before. And if all elements were chosen, I want to reset the used elements and start from the beginning.
Hope this makes sense.
I already have a function that chooses a random element from an array. But I also don't want it to choose elements that were chosen before unless all elements were already chosen.
Here is what I have got so far (credit to @Kelly):
var item = items[Math.floor(Math.random() * items.length)]
Upvotes: 2
Views: 2771
Reputation: 2840
Here's a solution that's pretty short. It uses randojs.com to simplify the randomness and make it more readable, though I kinda threw a wrench in the "easy to read" part by making it super short. If you're having trouble understanding, here are some resources that will help explain: declaring variables on one line, the ternary operator, different ways to declare a function, the pop() method, and randojs
You can actually make the JavaScript one line if you want by just removing the line break, but I chose to make it two for the sake of clarity and shorter lines.
var arr = ["one", "two", "three", "four"], shuffled = randoSequence(arr),
uniqueRandom = () => (shuffled.length ? shuffled : shuffled = randoSequence(arr)).pop().value;
<script src="https://randojs.com/1.0.0.js"></script>
<button onclick="console.log(uniqueRandom());">Log a unique random</button>
NOTE: this code won't work if you forget to import randojs with the script
tag, so make sure to paste that in the head of your HTML document if you want to use this code.
Upvotes: 0
Reputation: 11929
One possible way is prime walk.
First have the list of e.g. 500 items. Get the next prime number that is greater than 500. Here 503. Select random seed. This seed is any number that is constant for an user.
var prime = 503;
var list = ["item1", "item2", ... "item500"];
function pick_nth(seed, n, p, l) {
if(!n) return l[seed % p];
return pick_nth((seed + l.length) % p, n - 1, l);
}
Picking up n:th item from list is easy. For example:
pick_nth(seed, 0, prime, list); // first item
pick_nth(seed, 1, prime, list); // second item
...
pick_nth(seed, 499, prime, list); // 500th item
The order of the items returned are permutated by the seed.
Upvotes: 2
Reputation: 29116
The easiest way to handle this is:
function shuffle(array) {
var currentIndex = array.length, temporaryValue, randomIndex;
while (0 !== currentIndex) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
var items = ["alpha", "beta", "gamma", "delta", "epsilon"];
var index = Infinity;
function start() {
console.log("----- shuffling -----")
shuffle(items);
index = 0;
}
function nextItem() {
if (index >= items.length) {
//re-start
start()
}
//return current index and increment
return items[index++];
}
document.getElementById("click_me")
.addEventListener("click", function() {
console.log(nextItem())
})
<button id="click_me">Next random</button>
This can also be converted to a generator function
function shuffle(array) {
var currentIndex = array.length, temporaryValue, randomIndex;
while (0 !== currentIndex) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
function* random(array) {
let index = Infinity;
const items = array.slice(); //take a copy of the array;
while(true) {
if (index >= array.length) {
console.log("----- shuffling -----")
shuffle(items);
index = 0;
}
yield items[index++];
}
}
var items = ["alpha", "beta", "gamma", "delta", "epsilon"];
//start the generator
const generateRandom = random(items);
document.getElementById("click_me")
.addEventListener("click", function() {
console.log(generateRandom.next().value)
})
<button id="click_me">Next random</button>
Upvotes: 2
Reputation: 24955
You can try something like this:
data
and put it in chosenItems
data
reaches 0
, set chosenItems
or originalArray
as data and repeat process.Benefit of this approach would be,
function randomize(arr) {
let data = [...arr];
let chosenItems = [];
function getRandomValue() {
if (data.length === 0) {
data = chosenItems;
chosenItems = [];
}
const index = Math.floor(Math.random() * data.length);
const choice = data.splice(index, 1)[0];
chosenItems.push(choice);
return choice;
}
return {
randomItem: getRandomValue
}
}
const dummyData = [ 1,2,3,4,5 ];
const randomizeData = randomize(dummyData);
for (let i = 0; i< 10; i++) {
console.log(randomizeData.randomItem())
}
Upvotes: 3