Master JavaScript sort() method: compare function, sort numbers, strings, objects, toSorted(), stable sort, custom comparators, real-world examples, and interview Q&A.
sort() reorders the elements of an array in place. By default it converts elements to strings — which breaks numeric sorting. Mastering the compare function is the key to sorting anything correctly.
Visual: Default sort (lexicographic) vs Numeric sort
Array: [10, 9, 2, 21, 1]
Default sort (wrong for numbers!):
Converts to strings: "10", "9", "2", "21", "1"
Sorts alphabetically: ["1", "10", "2", "21", "9"]
Result: [1, 10, 2, 21, 9] ← WRONG!
Numeric sort (correct):
compareFn: (a, b) => a - b
Result: [1, 2, 9, 10, 21] ← CORRECT!
Sorting Numbers
const nums = [40, 100, 9, 2, 25, 10];
// ❌ Wrong — default (string) sort
const wrongSort = [...nums].sort();
console.log("Wrong:", wrongSort);
// ✅ Ascending
const asc = [...nums].sort((a, b) => a - b);
console.log("Ascending:", asc);
// ✅ Descending
const desc = [...nums].sort((a, b) => b - a);
console.log("Descending:", desc);
Wrong: [10, 100, 2, 25, 40, 9]
Ascending: [2, 9, 10, 25, 40, 100]
Descending: [100, 40, 25, 10, 9, 2]
Sorting Strings
const fruits = ["Banana", "apple", "Cherry", "Date", "elderberry"];
// Default sort (case-sensitive — uppercase first)
console.log([...fruits].sort());
// Case-insensitive sort
const caseInsensitive = [...fruits].sort((a, b) =>
a.toLowerCase().localeCompare(b.toLowerCase())
);
console.log(caseInsensitive);
// localeCompare (handles accents, locale-aware)
const words = ["résumé", "cafe", "naïve", "zebra"];
console.log(words.sort((a, b) => a.localeCompare(b)));
["Banana", "Cherry", "Date", "apple", "elderberry"]
["apple", "Banana", "Cherry", "Date", "elderberry"]
["cafe", "naïve", "résumé", "zebra"]
Sorting Arrays of Objects
const students = [
{ name: "Priya", grade: 88, age: 22 },
{ name: "Arjun", grade: 95, age: 20 },
{ name: "Meera", grade: 73, age: 23 },
{ name: "Rohit", grade: 88, age: 21 }
];
// Sort by grade ascending
const byGrade = [...students].sort((a, b) => a.grade - b.grade);
console.log(byGrade.map(s => `${s.name}: ${s.grade}`));
// Sort by grade descending, then name ascending (multi-key sort)
const multiSort = [...students].sort((a, b) => {
if (b.grade !== a.grade) return b.grade - a.grade; // grade desc
return a.name.localeCompare(b.name); // name asc
});
console.log(multiSort.map(s => `${s.name}: ${s.grade}`));
["Meera: 73", "Priya: 88", "Rohit: 88", "Arjun: 95"]
["Arjun: 95", "Priya: 88", "Rohit: 88", "Meera: 73"]
Sorting by Date
const events = [
{ name: "Launch", date: "2026-03-15" },
{ name: "Review", date: "2026-01-20" },
{ name: "Release", date: "2026-05-01" }
];
// Sort by date ascending
const sorted = [...events].sort((a, b) =>
new Date(a.date) - new Date(b.date)
);
sorted.forEach(e => console.log(`${e.date}: ${e.name}`));
2026-01-20: Review
2026-03-15: Launch
2026-05-01: Release
sort() vs toSorted() (ES2023)
sort() mutates the original. toSorted() returns a new sorted array:
const nums = [3, 1, 4, 1, 5, 9];
// sort() — mutates
const mutated = nums.sort((a, b) => a - b);
console.log("Original after sort:", nums); // [1, 1, 3, 4, 5, 9] — changed!
// toSorted() — non-mutating
const original = [3, 1, 4, 1, 5, 9];
const sorted = original.toSorted((a, b) => a - b);
console.log("Original after toSorted:", original); // [3, 1, 4, 1, 5, 9] — unchanged!
console.log("Sorted copy:", sorted); // [1, 1, 3, 4, 5, 9]
Original after sort: [1, 1, 3, 4, 5, 9]
Original after toSorted: [3, 1, 4, 1, 5, 9]
Sorted copy: [1, 1, 3, 4, 5, 9]
Stable Sort (Guaranteed in ES2019+)
Since ES2019, JavaScript's sort() is guaranteed to be stable — elements with equal sort keys maintain their original relative order:
const items = [
{ name: "A", priority: 2 },
{ name: "B", priority: 1 },
{ name: "C", priority: 2 },
{ name: "D", priority: 1 }
];
const sorted = items.sort((a, b) => a.priority - b.priority);
console.log(sorted.map(i => `${i.name}(${i.priority})`));
// B and D have same priority 1 → B still comes before D (stable!)
["B(1)", "D(1)", "A(2)", "C(2)"]
Real World Use Cases
// 1. Sort products by price (e-commerce)
const products = [
{ name: "Laptop", price: 80000 },
{ name: "Mouse", price: 500 },
{ name: "Monitor", price: 15000 }
];
const cheapFirst = products.toSorted((a, b) => a.price - b.price);
console.log(cheapFirst.map(p => `${p.name}: ₹${p.price}`));
// 2. Sort leaderboard
const leaderboard = [
{ player: "Alice", score: 4200, time: 120 },
{ player: "Bob", score: 4200, time: 115 },
{ player: "Carol", score: 5000, time: 200 }
];
const ranked = leaderboard.toSorted((a, b) =>
b.score !== a.score ? b.score - a.score : a.time - b.time
);
ranked.forEach((p, i) => console.log(`${i + 1}. ${p.player}: ${p.score} pts`));
// 3. Sort tags alphabetically
const tags = ["React", "javascript", "nodejs", "CSS", "HTML"];
const sorted = tags.toSorted((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" }));
console.log(sorted);
["Mouse: ₹500", "Monitor: ₹15000", "Laptop: ₹80000"]
1. Carol: 5000 pts
2. Bob: 4200 pts
3. Alice: 4200 pts
["CSS", "HTML", "javascript", "nodejs", "React"]
Common Mistakes
| ❌ Wrong | ✅ Correct | Why |
|---|
arr.sort() on numbers | arr.sort((a, b) => a - b) | Default sort is lexicographic — 100 < 9 as strings |
arr.sort() and using original | [...arr].sort(...) or arr.toSorted(...) | sort() mutates the original |
(a, b) => a > b as compareFn | (a, b) => a > b ? 1 : a < b ? -1 : 0 or just a - b for numbers | compareFn must return number, not boolean |
| Forgetting case insensitivity | .sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())) | Default string sort is case-sensitive |
Comparison: sort() vs toSorted()
| Feature | sort() | toSorted() (ES2023) |
|---|
| Mutates original | ✅ Yes | ❌ No |
| Returns | Same (mutated) array | New sorted array |
| Support | All browsers | Modern browsers / Node 20+ |
| Use when | You're OK mutating, or already have a copy | You want to preserve original |
Cheat Sheet
Numbers ascending: arr.sort((a, b) => a - b)
Numbers descending: arr.sort((a, b) => b - a)
Strings A→Z: arr.sort((a, b) => a.localeCompare(b))
Strings case-insensitive: arr.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()))
Objects by number prop: arr.sort((a, b) => a.price - b.price)
Objects by string prop: arr.sort((a, b) => a.name.localeCompare(b.name))
Multi-key: arr.sort((a, b) => a.grade !== b.grade ? b.grade - a.grade : a.name.localeCompare(b.name))
No mutation: arr.toSorted(compareFn) // ES2023
Sort copy: [...arr].sort(compareFn)
Interview Questions
Q1. What is the default behavior of sort() without a compare function?
It converts all elements to strings and sorts them lexicographically (alphabetically). This causes numeric sorting to fail — [10, 9, 2] becomes [10, 2, 9] because "10" < "2" alphabetically.
Q2. How do you sort an array of numbers correctly?
Provide a compare function: arr.sort((a, b) => a - b) for ascending, (a, b) => b - a for descending.
Q3. Does sort() mutate the original array?
Yes. sort() is an in-place sort — it modifies the original array and returns it. Use [...arr].sort(fn) or arr.toSorted(fn) (ES2023) to sort without mutation.
Q4. How do you sort an array of objects by a property?
users.sort((a, b) => a.age - b.age); // by number
users.sort((a, b) => a.name.localeCompare(b.name)); // by string
Q5. What is a stable sort and is JavaScript's sort stable?
A stable sort preserves the relative order of elements that compare as equal. Since ES2019, JavaScript's sort() is guaranteed to be stable in all modern engines.
Q6. What should a compare function return?
A negative number if a should come before b, zero if order doesn't matter, positive number if b should come before a. Never return a boolean.
Q7. What is toSorted() and how is it different from sort()?
toSorted() (ES2023) returns a new sorted array without modifying the original. It's the immutable/functional alternative to sort().
Key Takeaways
- Default
sort() is lexicographic — always provide a compare function for numbers sort() mutates the original array — use [...arr].sort() or toSorted() to avoid this- Compare function returns: negative →
a first, zero → tie, positive → b first (a, b) => a - b = ascending, (a, b) => b - a = descending- JavaScript's sort is stable since ES2019
- Use
localeCompare() for correct string/locale-aware sorting