Dave Birss
Dave Birss

Reputation: 23

How do I get elements from an array without repetition?

How do I get 5 elements from an array without repetition?

I'm trying to create an online version of story dice using Tumult Hype. All that I need to do is choose 5 image names from an array without repetition. But I just can't get it to work.

I've tried borrowing code from other stackoverflow answers and I can't get them working.

The code below is currently working but gives me repeats. How do I tinker with it to eliminate the repeats?

(You can see it in action here: https://davebirss.com/storydice/)

I know that it's probably verbose and inelegant. I only speak pidgin javascript, I'm afraid. And what I'm trying to do is currently beyond my ability.

Thank you so much in advance for your help.

var diceRoll = ['${resourcesFolderName}/story_dice1.png',
        '${resourcesFolderName}/story_dice2.png',
        '${resourcesFolderName}/story_dice3.png',
        ...,
        '${resourcesFolderName}/story_dice51.png']

function choose(n, arr) {
    while (arr.length > n) {
        var del = Math.floor(Math.random() * arr.length);
        arr = arr.filter(function(item, i) {
            return i !== del;
        });
    }
    return arr;}

var result1 = [choose(1, diceRoll)];
var result2 = [choose(1, diceRoll)];
var result3 = [choose(1, diceRoll)];
var result4 = [choose(1, diceRoll)];
var result5 = [choose(1, diceRoll)];

hypeDocument.getElementById("dice1").innerHTML = "<img src='"+result1+" 'height='125' width='125'>";
hypeDocument.getElementById("dice2").innerHTML = "<img src='"+result2+" 'height='125' width='125'>";
hypeDocument.getElementById("dice3").innerHTML = "<img src='"+result3+" 'height='125' width='125'>";
hypeDocument.getElementById("dice4").innerHTML = "<img src='"+result4+" 'height='125' width='125'>";
hypeDocument.getElementById("dice5").innerHTML = "<img src='"+result5+" 'height='125' width='125'>";

Update

Thank you all for your help. I'm sure all the answers were great but the snippet from U25lYWt5IEJhc3RhcmQg is the code that I managed to successfully incorporate. For the record, this is how I did it:

const rollTheDice = (arr, n) => {
  const randomN = [];
  while(randomN.length < n){
    const randomIndex = Math.floor(Math.random()*arr.length);
    randomN.push(arr[randomIndex]);
    arr.splice(randomIndex, 1);
  }
  return randomN;}

var result1 = (rollTheDice(images,1)); 
var result2 = (rollTheDice(images,1));
var result3 = (rollTheDice(images,1));
var result4 = (rollTheDice(images,1)); 
var result5 = (rollTheDice(images,1));

I've been repeatedly reloading the page and haven't seen any repeats yet. Perfect!

Upvotes: 2

Views: 459

Answers (5)

Yevhen Horbunkov
Yevhen Horbunkov

Reputation: 15530

I guess, the trickiest part here is not to waste the performance, limiting possible options to those, not previously chosen:

const images = ['a','b','c','d','e','f'];

const rollTheDice = (arr, n) => {
  const randomN = [];
  while(randomN.length < n){
    const randomIndex = Math.floor(Math.random()*arr.length);
    randomN.push(arr[randomIndex]);
    arr.splice(randomIndex, 1);
  }
  return randomN;
}

console.log(rollTheDice(images, 5));

Upvotes: 0

sstruct
sstruct

Reputation: 491

You want a random permutation which all elements is uniq and from one dataset, here is my implementation:

var array = [1, 2, 3, 4, 5, 6];

/**
 * uniqGet
 * @param {*} array source array
 * @param {*} num how many elements to get
 */
function uniqGet(array, num) {
  if (array.length < num) {
    throw new Error("num should less than options");
  }
  let res = [];
  while (num > 0) {
    let index = Math.floor(Math.random() * array.length);
    res.push(array[index]);
    array.splice(index, 1);
    num--;
  }
  return res;
}

let result = uniqGet(array, 3); // [x, y, z]

Upvotes: 0

Jack Bashford
Jack Bashford

Reputation: 44105

Copy it, then shuffle the copy, and remove the first element from the array each time:

const copy = [...diceRoll].sort(e => 0.5 - Math.random());

And in your choosing function:

const chosen = copy.shift();

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386654

You could take an array of indices and check if the index exist, then get a new index or push this index.

var length = 51,  // your count of items
    indices = [], // the result set with indices
    count = 5,    // the amount of wanted indices
    random;       // guess what?
    
while (indices.length < count) {                 // check length
    random = Math.floor(Math.random() * length); // get random value
    if (indices.includes(random)) continue;      // continue if already selected
    indices.push(random);                        // if not take it
}

console.log(indices);

Upvotes: 1

pRmdk
pRmdk

Reputation: 151

  1. Make a copy of diceRoll array (diceRollCopy).
  2. Use the new array(diceRollCopy) as argument of choose method.
  3. Whenever you get a result using choose method remove that result from the Copy array (diceRollCopy).
  4. You would need to reset the diceRollCopy to diceRoll after each set of results have been accessed.

Upvotes: 0

Related Questions