Home > Net >  Keep taking new element from an array
Keep taking new element from an array

Time:01-28

I have an array that whenever I fire a function called X I want to get random element from that array. The function will be called many times and every time I call that function I want to get element I didn't get before. I also need a reference to all elements so I can't be just removing from array Here are my 2 solutions I came up with:

  1. Make a function that gets a random index number excluding some numbers. Problem with function like this is that it basically works that whenever the number is equal to the excluded it just generates again until it gets the right one. So when array has like a 1000 elements but only like 10 are left it will be generating forever.

  2. Make a second array both would be the same at beginning. however I only remove from the second, and I can get random index from the second. Problem is whenever I remove that element using splice() from second array he is actually removed from both arrays. Is there a way to remove just from 1 array?

Am I just making some stupid mistakes and there is some simple solution that I am missing?

CodePudding user response:

You could generate a second array of indexes, shuffle it, and pop an index from it.

Once the index array is empty, refill it.

Something like

array = ['my', 'items', 'to', 'select']

// Fisher-Yates shuffle
function shuffle(ar) {
  for(let i = ar.length - 1; i >= 1; i--) {
    let temp = Math.floor(Math.random() * (i   1)); // select an item to swap with
    [ar[temp], ar[i]] = [ar[i], ar[temp]];          // do the swap
  }
}

let indices = [];

function nextItem() {
  if(indices.length == 0) {
    // generate the array of indexes 
    indices = array.map( (item, index) => index );
    shuffle(indices);
  }
  return array[indices.pop()];
}

// Call it a few times
for(let i = 0; i < 10; i  ) {
  console.log( nextItem() );
}

If you were concerned at all about performance, instead of popping from indices and regenerating a new array, you could instead have a "current index" that walks through the array, and when it gets to the end reshuffle.

CodePudding user response:

For solution 2, you need your second array to be copy of the original, not a reference to the original. Try this:

const originalArray = ['one', 'two', 'three']
let copyArray = [...originalArray]

CodePudding user response:

This is a perfect use case for a generator function. It will return undefined when you've exhausted the shuffled input array.

const input = [1,2,3]

function* shuffle(arr) {
  arr = [...arr] // make a shallow copy, to avoid mutating the original
  while(arr.length) yield arr.splice(Math.random()*arr.length|0, 1)[0]
}

let generator = shuffle(input);

console.log(generator.next().value)
console.log(generator.next().value)
console.log(generator.next().value)
console.log(generator.next().value)

  • Related