4

I'm trying to sort multiple arrays within an array (which also has to be shuffled). A simplified example is:

let toShuffle = [
[1, 2, 3, 4, 5],
[9, 8, 7, 6, 5],
[10, 67, 19 ,27]
...
];

const shuffled = shuffle(toShuffle);

// outout would look something like:
// [
//   [8, 6, 5, 7, 9],
//   [4, 3, 1, 5, 2],
//   [19, 26, 10, 67],
//   ...
// ]

This needs to be flexible, so any number of arrays with any amount of values should be valid.

Here is what I've tried:

function shuffle(a) {
  for (let e in a) {
    if (Array.isArray(a[e])) {
      a[e] = shuffle(a[e]);
    } else {
      a.splice(e, 1);
      a.splice(Math.floor(Math.random() * a.length), 0, a[e]);
    }
  }

  return a;
}

console.log("Shuffled: " + shuffle([
  [1, 2, 3, 4, 5],
  [5, 4, 3, 2, 1]
]))

But it's not working as intended. Is their an easier way to do this? Or is my code correct and just buggy.

KoderM
  • 382
  • 2
  • 15
  • You say `it's not working as intended` ... what's the intended result look like? – Bravo May 13 '22 at 00:14
  • I don't understand what you're doing in the `else` block. You're removing the current element of the array, but you never put it back. – Barmar May 13 '22 at 00:14
  • @Barmar I'm removing the e element from the array, then adding it back in a random position. – KoderM May 13 '22 at 00:15
  • No you're not. When you splice out the e element, `a[e]` is now the element that used to be `a[e+1]`. So you're moving that to a random position. You never add back the original `a[e]`. – Barmar May 13 '22 at 00:17
  • If you want to swap two array elements, do `temp = a[e]; random = Math.floor(Math.random() * a.length); a[e] = a[random]; a[random] = temp;` – Barmar May 13 '22 at 00:19
  • Is it always 2-dimensional, or is it variable levels of nesting? Can different elements be nested different amounts? – Barmar May 13 '22 at 00:21
  • There are plenty of fisher-rates shuffle implementations to be found. (e.g. https://stackoverflow.com/a/46161940/294949) Borrow one (that returns a new array) and map over it – danh May 13 '22 at 00:21

2 Answers2

2

You almost got it. The problem is that you are removing one item from an array, instead of capturing the removed item and them placing in a random position:

let toShuffle = [
[1, 2, 3, 4, 5],
[9, 8, 7, 6, 5],
[10, 67, 19 ,27]
];

function shuffle(a) {
  a = [...a]; //clone array
  for (let e in a) {
    if (Array.isArray(a[e])) {
      a[e] = shuffle(a[e]);
    } else {
      a.splice(~~(Math.random() * a.length), 0, a.splice(e, 1)[0]);
    }
  }

  return a;
}

console.log(JSON.stringify(shuffle(toShuffle)))
console.log(JSON.stringify(toShuffle))

[EDIT] The original code did not shuffle the parent array, if you need shuffle everything recursively, you can use this:

let toShuffle = [
[1, 2, 3, 4, 5],
[9, 8, 7, 6, 5],
[10, 67, 19 ,27]
];

function shuffle(a) {
  a = a.map(i => Array.isArray(i) ? shuffle(i) : i); //clone array
  a.sort(i => ~~(Math.random() * 2) - 1); //shuffle
  return a;
}

console.log("shuffled", JSON.stringify(shuffle(toShuffle)))
console.log("original", JSON.stringify(toShuffle))
vanowm
  • 9,466
  • 2
  • 21
  • 37
  • That code is pure genius. The way the sort method works! *Mind blown*. – KoderM May 13 '22 at 00:41
  • Also, just fwi. you have 2 unneeded next lines. You can simply do this: `return a.map(i => Array.isArray(i) ? shuffle(i) : i).sort(i => ~~(Math.random() * 2) - 1);` – KoderM May 13 '22 at 00:44
  • Oh, sure, this was for easier to understand how it works, on-liners are not practical for education purposes ;) – vanowm May 13 '22 at 00:46
  • Thats why they look so cool :) `shuffle=a=>a.map(i=>Array.isArray(i)?shuffle(i):i).sort(i=>~~(Math.random()*2)-1);` – KoderM May 13 '22 at 00:47
2

You can use Array.from() to create a new shallow-copied array and then to shuffle Array.prototype.sort() combined with Math.random()

Code:

const toShuffle = [
  [1, 2, 3, 4, 5],
  [9, 8, 7, 6, 5],
  [10, 67, 19 ,27]
]

const shuffle = a => Array.from(a).sort(() => .5 - Math.random())
const result = toShuffle.map(shuffle)

console.log('Shuffled:', JSON.stringify(result))
console.log('To shuffle:', JSON.stringify(toShuffle))
Yosvel Quintero
  • 18,669
  • 5
  • 37
  • 46