Question
Given the following JavaScript array:
const arr1 = ["a", "b", "c", "d"];
How can you randomly shuffle its elements? Explain how to shuffle the original array and, when needed, how to create a shuffled copy without changing the original array.
Short Answer
You will learn how to shuffle a JavaScript array with the Fisher-Yates algorithm, why it is preferred over sorting with a random comparator, and how to choose between mutating an array and returning a new shuffled copy.
Concept
Shuffling means rearranging every element in an array into a random order.
The standard solution is the Fisher-Yates shuffle (also called the Knuth shuffle). It walks backward through the array. At each position, it selects a random element from the portion that has not yet been fixed and swaps the two values.
This matters because a good shuffle should give every possible ordering an equal chance. For an array with four elements, there are 4! (24) possible orders. Fisher-Yates can produce those orders uniformly when given a uniform random-number source.
JavaScript's Math.random() is suitable for ordinary UI behavior, games, and randomized display order. It is not designed for security-sensitive work, such as generating passwords, cryptographic keys, or fair security tokens.
Mental Model
Imagine four cards lying on a table.
- Start with the last card position.
- Pick one card at random from all cards that have not been handled yet.
- Put that picked card into the current position.
- Move one position left and repeat.
Once a position has been filled, it is locked in place. This prevents earlier random choices from being disturbed and ensures each card gets one final position.
Syntax and Examples
The Fisher-Yates algorithm uses a loop, a random index, and destructuring assignment to swap elements.
function shuffleInPlace(array) {
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]];
}
return array;
}
const arr1 = ["a", "b", "c", "d"];
shuffleInPlace(arr1);
console.log(arr1); // Example: ["c", "a", "d", "b"]
j is chosen from 0 through i, inclusive:
const j = Math.floor(Math.random() * (i + 1));
Math.random() returns a number from inclusive up to exclusive. Multiplying by creates a range from up to, but not including, . converts it to an integer.
Step by Step Execution
Consider this array:
const values = ["a", "b", "c", "d"];
A possible Fisher-Yates run looks like this:
for (let i = values.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[values[i], values[j]] = [values[j], values[i]];
}
Suppose the random indexes are 1, then 0, then 1.
-
iis3, the last index. Supposejis1.- Swap indexes
3and1. ["a", "b", "c", "d"]becomes .
- Swap indexes
Real World Use Cases
Shuffling is useful whenever order should vary without losing or duplicating elements.
- Quiz applications: show questions in a different order for each attempt.
- Music players: randomize a playlist before playback.
- Flashcard tools: mix study cards to reduce memorizing their original order.
- Games: shuffle a deck of cards, loot options, or turn order.
- A/B testing and experiments: randomize a list before assigning items to groups, with additional care when fairness or repeatability matters.
- Data sampling: shuffle records before selecting a subset for a simple randomized workflow.
Real Codebase Usage
In production code, the choice between mutation and copying is important.
Mutate a local working array
When an array is created specifically for one operation, changing it in place is often simple and efficient.
const deck = createDeck();
shuffleInPlace(deck);
dealCards(deck);
Preserve shared state
If an array comes from application state, a function argument, a cache, or a constant configuration value, create a copy first.
const visibleProducts = shuffled(allProducts);
This is especially common in UI code, where unexpected mutation can make state updates difficult to track.
Inject randomness for tests
Tests often need predictable behavior. A reusable shuffle function can accept a random-number function:
function shuffleInPlace(array, random = Math.random) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
array;
}
Common Mistakes
Using sort() with a random comparator
This common shortcut should be avoided:
const shuffled = arr1.sort(() => Math.random() - 0.5);
Problems:
sort()mutatesarr1.- A sorting comparator is expected to behave consistently; random results violate that expectation.
- The resulting permutations are not uniformly distributed.
- Results can vary by JavaScript engine and implementation details.
Use Fisher-Yates instead.
Accidentally changing the original array
This mutates the original array:
const result = shuffleInPlace(arr1);
If arr1 must not change, copy it:
const result = shuffleInPlace([...arr1]);
Choosing the wrong random-index range
This excludes index i:
Comparisons
| Approach | Changes original array? | Uniform shuffle? | Recommended use |
|---|---|---|---|
| Fisher-Yates in place | Yes | Yes, with a uniform random source | Local arrays that can be changed |
Fisher-Yates on [...array] | No | Yes, with a uniform random source | Shared state and reusable input |
array.sort(() => Math.random() - 0.5) | Yes | No | Avoid |
| Selecting random items repeatedly | Depends | Often error-prone | Use only when sampling is the actual goal |
Shuffle versus random selection
A shuffle rearranges all elements. Random selection picks one or more elements.
oneLetter = arr1[.(.() * arr1.)];
Cheat Sheet
function shuffleInPlace(array) {
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]];
}
return array;
}
// Shuffle the existing array
shuffleInPlace(arr);
// Return a shuffled copy
const copy = shuffleInPlace([...arr]);
- Loop backward:
array.length - 1down to1. - Pick
jfrom0throughi:Math.floor(Math.random() * (i + 1)). - Swap
array[i]andarray[j]. - Empty and one-item arrays work without special handling.
- Fisher-Yates runs in O(n) time and uses extra space when shuffling in place.
FAQ
How do I shuffle an array without changing the original in JavaScript?
Copy it first, then shuffle the copy:
const shuffledArray = shuffleInPlace([...originalArray]);
Does JavaScript have a built-in shuffle() method?
No. JavaScript arrays do not have a built-in standard shuffle() method, so implement Fisher-Yates or use a trusted utility library when one is already part of the project.
Why does Fisher-Yates loop backward?
Each iteration places one random remaining element into its final position. Moving backward ensures the unshuffled portion is always indexes 0 through i.
Can a shuffle return the array in its original order?
Yes. The original order is one valid permutation, so a correct shuffle can occasionally produce it.
Is sort(() => Math.random() - 0.5) a valid shuffle?
No. It is biased, mutates the array, and relies on a comparator that does not follow sorting rules. Use Fisher-Yates.
Does shuffling copy objects inside an array?
No. [...array] creates a new outer array, but object elements remain references to the same objects. Shuffling changes their order, not the objects themselves.
Can I use Math.random() to shuffle passwords or secret tokens?
Mini Project
Description
Build a small playlist randomizer. It takes a list of songs, returns a shuffled playlist, and keeps the original library order unchanged. This mirrors common application behavior where source data is preserved while a temporary viewing or playback order is randomized.
Goal
Create a function that returns a shuffled playlist copy and displays both the original and shuffled lists.
Requirements
Use an array containing at least five song titles. Create a Fisher-Yates shuffle function. Do not change the original playlist array. Return and display a shuffled copy. Confirm that both arrays contain the same number of songs.
Keep learning
Related questions
Abort Ajax Requests with jQuery jqXHR.abort()
Learn how to cancel an in-progress jQuery Ajax request with jqXHR.abort(), handle abort status safely, and avoid stale UI updates.
Access the Correct this Inside a JavaScript Callback
Learn why JavaScript this changes in callbacks and how to preserve an object context using bind, arrow functions, and event handler patterns.
Add Key-Value Pairs to JavaScript Objects
Learn how to add key-value pairs to JavaScript objects with dot and bracket notation, dynamic keys, examples, and common mistakes.