-1

how to efficiently shuffle array of object looking something like this:

array = [
{from: "john", requestNum: 1},
{from: "jack", requestNum: 1},
{from: "jill", requestNum: 1},
{from: "john", requestNum: 2},
{from: "jane", requestNum: 1}]

I would like to shuffle these objects by random, but requests from same name must retain their relative order. So where ever Johns request number 2 ends up in shuffled array, it must be behind its request n1.

What I came up with and is functional, but probably really clumsy is:

for (let i = array.length - 1; i > 0; i--) {
  const j = Math.floor(Math.random() * (i + 1));
  [array[i], array[j]] = [array[j], array[i]];
}

console.log("SHUFFLED");
console.log(array);

const indexes = {};
for (let i = 0; i < array.length; i++) {
  if (indexes[array[i].from]) {
    indexes[array[i].from].push(i);
  } else {
    indexes[array[i].from] = [i];
  }
}

const requests = {};
for (let i = 0; i < array.length; i++) {
  if (requests[array[i].from]) {
    requests[array[i].from].push(array[i]);
  } else {
    requests[array[i].from] = [array[i]];
  }
}

function compare(a, b) {
  if (a.requestNum < b.requestNum) {
    return -1;
  }
  if (a.requestNum > b.requestNum) {
    return 1;
  }
  return 0;
}

for (var key in requests) {
  //console.log(requests[key]);
  if (requests[key].length > 0) {
    const sorted = requests[key].sort(compare);
    // console.log(sorted);
    requests[key] = sorted;
  }
}

const sortedArray = [];
for (var key in indexes) {
  for (let i = 0; i < indexes[key].length; i++) {
    sortedArray[indexes[key][i]] = requests[key][i];
  }
}
console.log("SORTED");
console.log(sortedArray);

Any ideas how to make it more efficient?

user3338991
  • 431
  • 2
  • 5
  • 9
  • Doing that will be really complex. How about splitting it into an array of arrays, where the inner arrays are requests from the same person? Then you use the answers to [this question](https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array) to shuffle the outer array. (I suppose you could `.flat` it afterward to get back to one dimension.) – T.J. Crowder Mar 03 '21 at 16:47
  • Great! What have you tried so far/what code have you written attempting to solve this problem? Where *specifically* are you getting stuck? Please edit the answers to these questions into the body of your question, as Stack Overflow *will not* write your code for you. See also: [How to Ask](https://stackoverflow.com/help/how-to-ask) – esqew Mar 03 '21 at 16:47
  • 1
    You have not defined what Objects are john, jack... elements – Mister Jojo Mar 03 '21 at 16:48

1 Answers1

0

The way I would attack it is to map all the objects to a user. Use a list of all the names, shuffle them. And than pop off the lists.

// Fisher-Yates Shuffle
function shuffle(array) {
  var currentIndex = array.length,
    temporaryValue, randomIndex;
  while (0 !== currentIndex) {
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex -= 1;
    temporaryValue = array[currentIndex];
    array[currentIndex] = array[randomIndex];
    array[randomIndex] = temporaryValue;
  }
  return array;
}

const array = [
  {from: 'john', requestNum: 1},
  {from: 'jack', requestNum: 1},
  {from: 'jill', requestNum: 1},
  {from: 'john', requestNum: 2},
  {from: 'jane', requestNum: 1},
];

const users = array.reduce((acc, obj) => {
  // store a reference to the name
  acc.names.push(obj.from);
  // store the reference to the object in an array
  acc.data[obj.from] = acc.data[obj.from] || [];
  acc.data[obj.from].push(obj);
  return acc;
}, {
  names: [],
  data: {}
});

// Shuffle the array of names
const shuffledNames = shuffle(users.names);

// Loop over the shuffled names and remove the items from the array for that user
const shuffledObjs = shuffledNames.map(name => users.data[name].shift());

console.log(shuffledObjs);

This code makes an assumption that array is in requestNum order. If that is not the case from the start, it needs a sort at the start.

epascarello
  • 204,599
  • 20
  • 195
  • 236