To shuffle a JavaScript array fairly and efficiently, use the Fisher-Yates (Knuth) shuffle algorithm. This method ensures every permutation is equally likely, unlike simpler approaches (e.g., sort()
with Math.random()
), which introduce bias.
Correct Implementation
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
// Pick a random index from 0 to i (inclusive)
const j = Math.floor(Math.random() * (i + 1));
// Swap elements at positions i and j
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
// Usage:
const myArray = [1, 2, 3, 4, 5];
shuffleArray(myArray);
console.log(myArray); // e.g., [3, 1, 5, 2, 4]
Why Avoid array.sort(() => Math.random() - 0.5)
?
This method is biased because:
- The sorting algorithm compares elements multiple times with inconsistent randomness.
- Some elements have higher probabilities of ending up in specific positions.
Key Features of Fisher-Yates
- In-place shuffling: Modifies the original array (use
[...array]
to create a copy first if needed). - Linear time complexity: O(n) efficiency.
- Uniform distribution: All permutations are equally likely.
Shuffle Without Mutating the Original Array
Create a copy to preserve the original:
function shuffleCopy(array) {
const copy = [...array];
for (let i = copy.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[copy[i], copy[j]] = [copy[j], copy[i]];
}
return copy;
}
// Usage:
const original = [1, 2, 3];
const shuffled = shuffleCopy(original);
console.log(original); // [1, 2, 3] (unchanged)
console.log(shuffled); // e.g., [3, 1, 2]
Comparison Table
Method | Fair Shuffle | Time Complexity | Modifies Original Array |
---|---|---|---|
Fisher-Yates | ✅ Yes | O(n) | ✅ Yes (optional) |
sort() + random() | ❌ No | O(n log n) | ✅ Yes |
Visual Example of Fisher-Yates
Initial Array: [A, B, C, D]
Steps:
- Swap
D
(index 3) with a random element (e.g.,B
at index 1) →[A, D, C, B]
- Swap
C
(index 2) with a random element (e.g.,A
at index 0) →[C, D, A, B]
- Swap
D
(index 1) with itself → no change.
Result: [C, D, A, B]
Browser/Environment Support
Works in all modern browsers (ES6+). For legacy environments, replace the destructuring assignment with a temporary variable:
// Swap without destructuring:
const temp = array[i];
array[i] = array[j];
array[j] = temp;
Summary
Use the Fisher-Yates algorithm for unbiased shuffling. Avoid Math.random()
-based sorting for critical applications.