1

In my program, when the user clicks a button, an item (a string) from an array gets randomly selected, and is then outputted to the user.

My goal is to introduce some kind of "remove this item from the array after displaying to the user" functionality, to prevent repeat outputs. That way, each time they're viewing a fresh item, and not seeing the same ones over and over again.

Here is my v1 version of coding this:

Array1 = [] // the full array of all items is contained in here.
Array2 = [] // this is the post-display holding bay for items the user has seen already.

After outputting an item to the user from Array 1? That item will be added to Array 2. BEFORE displaying any array items to a user? It will cross-reference the randomly selected item against the full list of items in Array2, basically using IF/ELSE IF logic. Something like this:

for (let i = 0; i < NumberOfArray2Items; i++) {
  if (Array1ItemToDisplay === Array2[i]) {
    NumberOfMatches = j++ // basically, increment a numerical variable, so that any value
      // greater than 0 indicates, yes, it's been outputted to the user before
  }
}

if (NumberOfMatches === 0) {
  DisplayCurrentArrayItem();
} else if (NumberOfMatches > 0) {
  SelectAnotherArrayItemToDisplay();
}

That's a rough version of what I have in mind, and frankly it sounds like a terrible solution because it requires two very lengthy programmatic steps:

  1. Looping over every single item in Array2 for cross-referencing
  2. Potentially running that dozens, maybe hundreds of times, if it continues to find matches each time it runs that block of code.

Is there some simpler, faster method of cross-refencing an item against an entire array? Or perhaps cross-referencing both arrays against each other, to only leave the remaining items that don't match, and then select from that intermediate list of items that hasn't yet been displayed?

These arrays will contain tens of thousands of items, so my concern is, as presently written, this code will take forever to execute. Is there some simpler, faster, more efficient solution I'm not seeing?

Fraser
  • 15,275
  • 8
  • 53
  • 104
king_anton
  • 167
  • 8
  • 2
    Compared to the time it takes a user to type a key and the system to draw a string, searching an array of 10,000 items is quite fast. Do the brute force method and fix it when you have an actual performance problem, not just one you think of. – stark Sep 24 '22 at 19:37
  • 1
    Does this answer your question? [How to efficiently randomly select array item without repeats?](https://stackoverflow.com/questions/17891173/how-to-efficiently-randomly-select-array-item-without-repeats) – Heretic Monkey Sep 24 '22 at 19:47
  • I just ran a test of this code, using 100,000 items, because I was concerned about execution time of this code when the array sizes become massive -- and I am absolutely AMAZED at how fast it works. It's literally instantaneous. Like, you cannot detect the delay. It cross-references the item against 99,999 entries, in milliseconds! That's crazy! – king_anton Sep 24 '22 at 22:23
  • I continued pushing this further to stress-test the hell out of it, and I found that at the 500,000 item level? THEN I start to see some performance issues that impact the UX and cause some delays. Even here they aren't huge, but that's kinda the point where it starts to become noticeable. Considering that it will realistically take probably several years to get to that level? That's one of those issues where, I'll get there when I get there. (I'm using the cross-referencing Array1 against Array2 method. For the current program, this way worked best.) – king_anton Sep 24 '22 at 22:42
  • In the future I might switch to some other method of doing this, to avoid those performance issues. Funny thing is though? At that size where performance issues from cross-referencing DO start to arise? The array size will be so massive that removing already viewed entries might not even become necessary. Because the sheer amount of time required to view them all means it's just realistically not that likely an occurrence that they'll see tons of repeats. So in a way the problem might just solve itself over time. – king_anton Sep 24 '22 at 22:46

1 Answers1

0

Here you go, each time the button is clicked a random element from the array is shown. That element is then removed - so no duplicates are shown.

const data = ['foo', 'bar', 'bat', 'baz'];

document.getElementById('btn').addEventListener("click", e => {
  if(!data.length) return;
  const idx = Math.floor(Math.random() * data.length);
  console.log(data[idx]);
  data.splice(idx, 1);
});
<button id=btn>click</button>

I also added a check to make sure that when there are no unique items left nothing happens, not sure if this is what you wanted or not. If not - remove the line if(!data.length) return;

Fraser
  • 15,275
  • 8
  • 53
  • 104
  • Would it not be better/faster/more efficient to simply shuffle the array and use `pop`? – Heretic Monkey Sep 24 '22 at 19:46
  • @HereticMonkey - Why are you trying to optimise it? Also - I don't think so - the standard shuffle `Fisher-Yates` is going to be way more intensive than generating a random index. See https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array – Fraser Sep 24 '22 at 19:46
  • The OP asked for a "simpler, faster, more efficient solution"... – Heretic Monkey Sep 24 '22 at 19:49
  • Thanks! What I had in mind. There is one level of complication to this program I'm writing, and that's that this Array1 pool will continually be increasing in size over time. It's not like a static array of 10,000 items, forever until the end of time. Today it might be 10,000, next week it might be 12,314 items. Will this method still work on such an array? Or do I need to find a way to re-write the code to account for that? (This complication is why my first potential solution idea was cross-referencing each item against the array of already-ouputted items) – king_anton Sep 24 '22 at 19:49
  • @Heretic Monkey - Which I provided - shuffle isn't. – Fraser Sep 24 '22 at 19:50
  • @king_anton it will work on an array of any size (RAM dependant obviously) – Fraser Sep 24 '22 at 19:51
  • So this line of code here (data.splice(idx, 1);), this will basically remove the specific item being outputted to the user, regardless of its future position in the array, right? What I'm concerned about is, if adding more array items before and after this item's original position, might cause issues because the positioning changes. That code targets only the one specific item, removes it, and basically patches the array around it -- so it'll work fine as the array grows in size and order over time? Just want to be sure. Thanks! – king_anton Sep 24 '22 at 19:52
  • @king_anton - Yes - `data.splice(idx, 1)` will remove the item that was output - and each time this is random based on the current array state/length. So if you pushed more/new data in it would still work. – Fraser Sep 24 '22 at 19:55