15

The code below works for a normal array but not with an array with object does anybody knows how to do this?

const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

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

const result = shuffle(array);

console.log(JSON.stringify(result));
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
Arne
  • 171
  • 1
  • 1
  • 4
  • 1
    can you elaborate more what kind of structure of array. – Pankaj Bisht Mar 29 '18 at 11:51
  • 5
    Possible duplicate of [How to randomize (shuffle) a JavaScript array?](https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array) – Pankaj Bisht Mar 29 '18 at 11:52
  • What do you mean by "array with object" ? – Máté Safranka Mar 29 '18 at 11:54
  • 2
    This code is agnostic to the content of the array. Replace all numbers to objects - { id: 1 }, { id: 2 }, etc... and you can see it. – Ori Drori Mar 29 '18 at 11:54
  • In my experience, this works except for this one case: an associative array where every element is an array of objects. If I pass one of those array elements (which is itself an array) to this function, then the array is not shuffled. However, if you modify the code to make a copy of the array and then shuffle that and return it, that will work. You must use a legitimate array copy method like: newArray = [...array], in other words, newArray = array won't work. – Michael McCrickard Nov 25 '19 at 17:13

3 Answers3

52

Try sorting like this snippet:

console.log( [
    { some: 1 },
    { some: 2 },
    { some: 3 },
    { some: 4 },
    { some: 5 },
    { some: 6 },
    { some: 7 },
  ]
  .sort( () => Math.random() - 0.5) );

In reponse to Martin Omanders comment: here's a shuffle method according to the Fisher-Yates algorithm

const result = document.querySelector("pre");
for (let i=0; i<20; i+=1) {
  result.textContent += 
    JSON.stringify(shuffleFisherYates([0,1,2,3,4,5,6,7,8,9])) + '\n';
}

function shuffleFisherYates(array) {
  let i = array.length;
  while (i--) {
    const ri = Math.floor(Math.random() * i);
    [array[i], array[ri]] = [array[ri], array[i]];
  }
  return array;
}
<pre></pre>

Which may be condensed to a one liner (note: this one liner will not compile in the Google Closure Compiler with level advanced):

const shuffle = array => 
  [...Array(array.length)]
    .map((el, i) => Math.floor(Math.random() * i))
    .reduce( (a, rv, i) => ([a[i], a[rv]] = [a[rv], a[i]]) && a, array);
const result = document.querySelector("pre");
for (let i=0; i<100; i+=1)
  result.textContent +=
   JSON.stringify(shuffle([0,1,2,3,4,5,6,7,8,9])) + '\n';
<pre></pre>
KooiInc
  • 119,216
  • 31
  • 141
  • 177
  • 3
    Others have pointed out that this solution (while cute and good enough for some uses) isn't very random: https://stackoverflow.com/a/18650169/455313 – Martin Omander Feb 14 '19 at 18:50
3

Here is one more example based on lodash _.shuffle.

const array = [
    { some: 1 },
    { some: 2 },
    { some: 3 },
    { some: 4 },
    { some: 5 },
    { some: 6 },
    { some: 7 },
  ];
console.log(_.shuffle(array));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
Penny Liu
  • 15,447
  • 5
  • 79
  • 98
0

If you need to shuffle a collection of element nodes (in the DOM), convert it to an array first:

var wrapper = document.getElementById('someid');
var array = Array.prototype.slice.call(wrapper.children);

Shuffling a HTMLCollection object (as returned by children) directly doesn't work even if you can iterate the collection with a for loop. This stumbled me for a while.

hansfn
  • 530
  • 4
  • 18