How does randomizing two arrays without mismatching in JavaScript work?

Viewed 43

Trying to understand an answer from a previous question.

I found this question and subsequently the answer provided by @obscure, which I was able to modify based on what I needed. It worked perfectly! So I'm just trying to make sure I learn and understand exactly what's happening here.

Best I understand currently:

tempA = fileNames[a]; and tempA = trackTitles[a]; are temporarily storing the current location in the iteration, but I'm not sure what happens at fileNames[tempB] = tempA; and trackTitles[tempB] = tempA;.

tempB = Math.floor(Math.random() * fileNames.length); is generating a random index within the questions array.

fileNames[a] = fileNames[tempB]; and trackTitles[a] = trackTitles[tempB]; are swapping the current index with the randomly generated index.

Any help is appreciated!

this.fileNames = ["fileA", "fileB", "fileC"];
this.trackTitles = ["titleA", "titleB", "titleC"];

function shuffle() {
  var tempA;
  var tempB;
  for (var a = 0; a < fileNames.length; a++) {
    tempA = fileNames[a];
    tempB = Math.floor(Math.random() * fileNames.length);
    fileNames[a] = fileNames[tempB];
    fileNames[tempB] = tempA;

    tempA = trackTitles[a];
    trackTitles[a] = trackTitles[tempB];
    trackTitles[tempB] = tempA;
  }
}

shuffle();
console.log(fileNames);
console.log(trackTitles);

1 Answers

The code iterates over the array and for each element it stores the element with index a in the variable tempA and a random index is generated and stored in tempB hence the line tempB = Math.floor(Math.random() * fileNames.length);. In the following lines the element at the current Index a and the element at the randomly generated index tempB are swapped, same with thing with the second array. The randomly generated index is used on both arrays so it doesn't missmatch, which is exactly what you want.

This algorithm has a problem tho, because it could potentially reverse itself: Lets say you have the following array ["a", "b", "c", "d"] and use the above algorithm on that array, then you could potentially swap a → b, b → a, c → d and d → c, ending up with the same array as you began with.

I would recommend you to use the Fisher-Yates Shuffle algorithm. This algorithm also has the chance to return the exacted same array, but it at least doesn't reverse the steps it did before. The idea of the algorithm is that the array builds itself up from a pool of elements you can randomly choose from, and if that element has been chosen, it can't be chosen again and is removed from that pool (It actually works like selection-sort, if you are familiar with that, but instead of choosing the max or min element of the rest, you just choose a random one).

Implementation:

this.fileNames = ["fileA", "fileB", "fileC"];
this.trackTitles = ["titleA", "titleB", "titleC"];

function shuffle() {
  let currentIndex = this.fileNames.length-1, randomIndex;
  
  while (currentIndex > 0) {
    randomIndex = Math.floor(Math.random() * (currentIndex+1));

    [this.fileNames[currentIndex], this.fileNames[randomIndex]] = 
    [this.fileNames[randomIndex], this.fileNames[currentIndex]];
    
    [this.trackTitles[currentIndex], this.trackTitles[randomIndex]] = 
    [this.trackTitles[randomIndex], this.trackTitles[currentIndex]];
    
    currentIndex--;
  } 
}

shuffle();
console.log(fileNames);
console.log(trackTitles);

You could modify the algorithm though so that indices don't choose themselves. This way, you won't get the same exact array.

this.fileNames = ["fileA", "fileB", "fileC", "fileD", "fileE", "fileF"];
this.trackTitles = ["titleA", "titleB", "titleC", "titleD", "titleE", "titleF"];

const generateRandom = (n, ex) => {
  const r = Math.floor(Math.random() * (n+1))
  return r == ex ? generateRandom(n, ex) : r;
}

function shuffle() {
  let currentIndex = this.fileNames.length-1, randomIndex;
  
  while (currentIndex > 0) {
    randomIndex = generateRandom(currentIndex, currentIndex);

    [this.fileNames[currentIndex], this.fileNames[randomIndex]] = 
    [this.fileNames[randomIndex], this.fileNames[currentIndex]];
    
    [this.trackTitles[currentIndex], this.trackTitles[randomIndex]] = 
    [this.trackTitles[randomIndex], this.trackTitles[currentIndex]];
    
    currentIndex--;
  }
}

shuffle();
console.log(fileNames);
console.log(trackTitles);

Related