Russell Russell - 5 months ago 9
Javascript Question

Javascript: How to efficiently randomly select array item without repeats

I'm aware that this question is around in many guises but I have not been able to find an answer relating to my specific issue of efficiency.

I have the below code that works just fine.

I have a 10 item array from which I randomly select an item (on enter key press). The code keeps an array of the 5 most recent choices which cannot be randomly selected (to avoid too much repetition over time).

If the chooseName() function initially selects a name that has been used in the recent 5 goes it simply breaks and calls itself again, repeating until it finds a "unique" name.

I have two questions:


  1. Is it correct to say this is a "recursive function"?

  2. I am worried that theoretically this could keep looping for a long time before finding a unique name - is there a more efficient way to do this?



Thank you for any help.

var a = ["Roger", "Russell", "Clyde", "Egbert", "Clare", "Bobbie", "Simon", "Elizabeth", "Ted", "Caroline"];
var b = [];

var chooseName = function () {
var unique = true;
b.length = 5;
num = Math.floor(Math.random() * a.length);
name = a[num];
for (i = 0; i < a.length; i++) {
if (b[i] == name) {
chooseName();
unique = false;
break;
}
}
if (unique == true) {
alert(name);
b.unshift(name);
}
}


window.addEventListener("keypress", function (e) {
var keycode = e.keyCode;
if (keycode == 13) {
chooseName();
}
}, false);

Answer

Whenever an item is selected, move it to the back of the array and randomly select from a slice of the original array array.slice(0, -5).

var a = ["Roger", "Russell", "Clyde", "Egbert", "Clare", "Bobbie", "Simon", "Elizabeth", "Ted", "Caroline"];

var chooseName = function () {
    var unique = true;
    num = Math.floor(Math.random() * a.length - 5);
    name = a.splice(num,1);
    a.push(name);
}


window.addEventListener("keypress", function (e) {
    var keycode = e.keyCode;
    if (keycode == 13) {
        chooseName();
    }
}, false);

EDIT: This also has the side-effect of not giving whichever variables happen to tail the list the unfair disadvantage that they won't be considered in the first N calls. If that's a problem for you, maybe try hold a static variable somewhere to keep track of the size of the slice to use and max it out at B (in this case, 5). e.g.

var a = ["Roger", "Russell", "Clyde", "Egbert", "Clare", "Bobbie", "Simon", "Elizabeth", "Ted", "Caroline"];
B = 5; //max size of 'cache'
N = 0;

var chooseName = function () {
    var unique = true;
    num = Math.floor(Math.random() * a.length - N);
    N = Math.min(N + 1, B);
    name = a.splice(num,1);
    a.push(name);
}