How to Sort an Array Without Mutation in JavaScript
A critical characteristic of the Array.prototype.sort() method is that it is mutating—it sorts the elements of an array in place, directly changing the original array. This can lead to unexpected bugs in modern applications where immutable data is often preferred.
This guide will teach you the modern, built-in method for sorting an array without mutation using Array.prototype.toSorted(). We will also cover the classic and still widely-used "copy-then-sort" approach for environments where toSorted() is not available.
The Core Problem: sort() Mutates the Original Array
When you call .sort() on an array, the original array is rearranged. This can be a problem if other parts of your application rely on the original order of that array.
Example of problem:
// Problem: The `sort()` method changes the `originalArray`.
let originalArray = ['cherry', 'apple', 'banana'];
console.log('Before sort:', originalArray);
// This modifies `originalArray` directly.
let sortedArray = originalArray.sort();
console.log('After sort (new variable):', sortedArray);
console.log('After sort (original variable):', originalArray); // This has changed!
Output:
Before sort: [ 'cherry', 'apple', 'banana' ]
After sort (new variable): [ 'apple', 'banana', 'cherry' ]
After sort (original variable): [ 'apple', 'banana', 'cherry' ]
Both variables now point to the same, sorted array.
The Modern Solution (Recommended): toSorted()
Modern JavaScript (ES2023) introduced the Array.prototype.toSorted() method, which is the direct, immutable equivalent of sort(). It returns a new, sorted array and leaves the original array completely untouched.
This is the recommended best practice in modern environments.
let originalArray = ['cherry', 'apple', 'banana'];
// toSorted() returns a new, sorted array.
let sortedArray = originalArray.toSorted();
console.log('Sorted Array:', sortedArray); // Output: ['apple', 'banana', 'cherry']
console.log('Original Array:', originalArray); // Output: ['cherry', 'apple', 'banana'] (Unchanged!)
Output:
Sorted Array: ['apple', 'banana', 'cherry']
Original Array: ['cherry', 'apple', 'banana']
This method is clean, explicit, and aligns with modern functional programming principles.
The Classic Solution: The "Copy-then-Sort" Pattern
If you need to support older browsers or JavaScript runtimes that do not have toSorted(), the standard practice is to manually create a copy of the array before you sort it. The modern spread syntax (...) is the most common and readable way to create this shallow copy.
let originalArray = ['cherry', 'apple', 'banana'];
// 1. Create a shallow copy of the array using the spread syntax.
// 2. Call the mutating .sort() method on the copy.
let sortedArray = [...originalArray].sort();
console.log('Sorted Array:', sortedArray); // Output: ['apple', 'banana', 'cherry']
console.log('Original Array:', originalArray); // Output: ['cherry', 'apple', 'banana'] (Unchanged!)
Output:
Sorted Array: ['apple', 'banana', 'cherry']
Original Array: ['cherry', 'apple', 'banana']
Other ways to create a copy include originalArray.slice() or Array.from(originalArray), but the spread syntax is generally the most idiomatic.
Sorting an Array of Numbers (Without Mutation)
Both toSorted() and the "copy-then-sort" pattern require a compare function to sort numbers correctly. The default sort behavior treats numbers as strings, leading to incorrect results (e.g., [1, 10, 2] instead of [1, 2, 10]).
Solution with toSorted
let numbers = [10, 5, 1, 7];
// Pass the compare function directly to toSorted().
let sortedNumbers = numbers.toSorted((a, b) => a - b);
console.log(sortedNumbers); // Output: [1, 5, 7, 10]
console.log(numbers); // Output: [10, 5, 1, 7] (Unchanged!)
Output:
[1, 5, 7, 10]
[10, 5, 1, 7]
Solution with "Copy-then-Sort"
let numbers = [10, 5, 1, 7];
let sortedNumbers = [...numbers].sort((a, b) => a - b);
console.log(sortedNumbers); // Output: [1, 5, 7, 10]
console.log(numbers); // Output: [10, 5, 1, 7] (Unchanged!)
Output:
[1, 5, 7, 10]
[10, 5, 1, 7]
Conclusion
Sorting an array without changing the original is a crucial practice for writing predictable, bug-free code.
- The modern and recommended best practice is to use the
array.toSorted()method. It is explicitly designed for immutable sorting and is the clearest expression of your intent. - For older environments, the standard "copy-then-sort" pattern using the spread syntax is the best alternative:
let sorted = [...myArray].sort(). - Remember to always provide a compare function (e.g.,
(a, b) => a - b) when sorting numbers.