DEV Community

Cover image for How to shuffle an array in JavaScript
James Bubb
James Bubb

Posted on

How to shuffle an array in JavaScript

In this article we’ll take a look at a couple of ways to shuffle an array in JavaScript.

Custom sort

The first and simplest way to shuffle an array in JavaScript is to provide a custom function to a .sort().

const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const shuffledArray = array.sort((a, b) => 0.5 - Math.random());
Enter fullscreen mode Exit fullscreen mode

As the function we pass to .sort() is looking for either a positive or negative number to either move the item ‘up’ or ‘down’ in the array, each item has a chance of being moved in either direction giving us a shuffled array of items.

This works for a rough-and-ready approach but might not give you a truly random shuffle.

If you do a bit of research in to the above technique (check out this article), you’ll see that using the custom sort function is flawed (although I can’t give you a definitive answer as to why that is!).

If you need to shuffle an array and have a truly random distribution of items, you need to implement the Fisher-Yates algorithm.

The Fisher-Yates algorith

Luckily for us, it's not too complicated:

const shuffleArray = array => {
  for (let i = array.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    const temp = array[i];
    array[i] = array[j];
    array[j] = temp;
  }
}
Enter fullscreen mode Exit fullscreen mode

As you can see it’s just a case of looping through the array (from the end to the start) and picking a random item from the array and swapping it with the item in the current iteration.

You can use the above function to shuffle an array in JavaScript and get a random result each time.

Top comments (11)

Collapse
 
wannabehexagon profile image
ItsThatHexagonGuy • Edited

Tip: You can swap values using array destructuring:

[array[i], array[j]] = [array[j], array[i]];
Enter fullscreen mode Exit fullscreen mode

I wouldn't recommend this kind of thing in a production codebase. If you have experience with lists in Python this might seem familiar though.

Collapse
 
drebot92 profile image
Vladyslav • Edited
function shuffle(array) {
  const newArray = [...array]
  const length = newArray.length

  for (let start = 0; start < length; start++) {
    const randomPosition = Math.floor((newArray.length - start) * Math.random())
    const randomItem = newArray.splice(randomPosition, 1)

    newArray.push(...randomItem)
  }

  return newArray
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
patarapolw profile image
Pacharapol Withayasakpunt

Is the first method really flawed?

Also, rather than Math.random(), I can always use crypto.getRandomValues()

developer.mozilla.org/en-US/docs/W...

Collapse
 
codebubb profile image
James Bubb

Yeah, apparently - I think it boils down to a question of sort() having a strong bias for keeping elements in their initial positions (there's a bit of a discussion about it on one of the answers here: stackoverflow.com/questions/245095...).

Thanks for the tip regarding .getRandomValues(). How would that work with strings or objects?

Collapse
 
patarapolw profile image
Pacharapol Withayasakpunt • Edited

I read the solution part of javascript.info/array-methods#shuf..., but not really understand the logic, or is it the fault of JS Engine?

For .getRandomValues(), there are two more methods of shuffling, but of course, not performant, even if your first method is not faulty.

function shuffle1(arr: any[]): any[] {
    return Array(arr.length).fill(null)
        .map((_, i) => [Math.random(), i])
        .sort(([a], [b]) => a - b)
        .map(([, i]) => arr[i])
}

function shuffle2(arr: any[]): any[] {
    const ord = new Uint8Array(arr.length)
    crypto.getRandomValues(ord)

    return ord
        .map((a, i) => [a, i])
        .sort(([a], [b]) => a - b)
        .map(([, i]) => arr[i])
}
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
codebubb profile image
James Bubb

Haha! I like this from that site: But due to the utter randomness of the comparison the black box goes mad, and how exactly it goes mad depends on the concrete implementation that differs between engines.

So yes, I think it's down to the JavaScript runtime itself where the problem lies.

Thread Thread
 
tvogel profile image
Tilman Vogel

Well, the generic Array.sort() algorithm puts several requirements on the comparison function, see developer.mozilla.org/en-US/docs/W... which in turn it may use to implement the sorting algorithm. In the "Custom sort" approach above, in particular the requirement of the comparison to be "stable" is violated.
So, you are breaking the Array.sort() contract and may receive any result. In the worst case, the algorithm might not even terminate.

Collapse
 
jonrandy profile image
Jon Randy 🎖️ • Edited

Fisher-Yates is orders of magnitude faster, and gives a less biased result. There is an excellent visual write-up of the algorithm here.

Collapse
 
dmitryame profile image
Dmitry Amelchenko

How about creating a new ordered Hash key value data structure, and use randomly generated UUID as a key?

Collapse
 
dmitryame profile image
Dmitry Amelchenko • Edited

This exports a randomly sorted array of questions.

var map = new Map()
map.set(uuid4(), "What inspires you?")
map.set(uuid4(), "Who is someone in your life you consider a hero and why?")
map.set(uuid4(), "What is your favorite snack?")
map.set(uuid4(), "What words of wisdom do vou live by?")
map.set(uuid4(), "What is your favorite holiday tradition?")
map.set(uuid4(), 'Please complete this sentence."Happiness is..."')

export const QUESTIONS = [...map]
  .sort(([a], [b]) => String(a).localeCompare(b))
  .map((a) => a[1]) 
Enter fullscreen mode Exit fullscreen mode
Collapse
 
zweroboy1 profile image
zweroboy1

Why does JS have no standart method Array.prototype.shuffle()?