Why Array.sort() Gives Wrong Results Without a Compare Function
Sorting data is one of the most common operations in programming.
Whether you're building:
- Ecommerce applications
- Analytics dashboards
- Search results
- Financial systems
- Reporting tools
- Data visualizations
you'll frequently need to arrange data in a meaningful order.
JavaScript provides a built-in solution:
array.sort()
At first glance, it appears simple.
Consider:
const numbers = [1, 2, 3];
numbers.sort();
Output:
[1, 2, 3]
Everything seems correct.
However, many developers eventually encounter a surprising result:
const numbers = [1, 10, 2, 5];
numbers.sort();
console.log(numbers);
Output:
[1, 10, 2, 5]
Most people expect:
[1, 2, 5, 10]
Instead, JavaScript produces an apparently incorrect order.
This behavior often leads developers to believe:
Array.sort() is broken
In reality:
Array.sort() is working exactly as designed.
The issue lies in understanding how JavaScript performs sorting when no compare function is provided.
In this guide, you'll learn why unexpected sorting occurs, how compare functions work, and how to sort numbers, strings, dates, and objects correctly.
What You Will Learn From This Article
After reading this guide, you'll understand:
- How
Array.sort()works internally. - Why numeric sorting often fails.
- The default string comparison behavior.
- How compare functions operate.
- Sorting numbers correctly.
- Sorting objects and dates.
- Performance and best practices.
Understanding the Default Behavior
Most developers assume:
sort()
means:
Sort Numerically
It does not.
By default, JavaScript converts values to strings and performs lexicographical sorting.
Example:
[1, 10, 2]
becomes:
["1", "10", "2"]
before comparison.
What Is Lexicographical Sorting?
Lexicographical sorting is essentially dictionary ordering.
Example:
Apple
Banana
Cherry
This works perfectly for text.
However, numbers behave differently.
Why Numbers Sort Incorrectly
Consider:
const numbers = [1, 10, 2];
numbers.sort();
JavaScript compares:
"1"
"10"
"2"
Character by character.
Comparison order:
"1" < "10"
Correct.
Then:
"10" < "2"
because:
1 < 2
when comparing the first character.
Result:
[1, 10, 2]
No bug exists.
Only string comparison.
Another Surprising Example
Input:
[100, 20, 3]
Converted internally:
["100", "20", "3"]
Sorted result:
[100, 20, 3]
Expected numeric order:
[3, 20, 100]
Actual order follows string rules.
How the Compare Function Works
A compare function tells JavaScript how two values should be ordered.
Example:
array.sort((a, b) => {
return a - b;
});
Now sorting becomes numeric.
Understanding Compare Function Results
The function:
(a, b) => a - b
returns:
Negative Number
a before b
Positive Number
a after b
Zero
No Change
This simple rule powers all custom sorting.
Sorting Numbers Correctly
Ascending order:
const numbers = [1, 10, 2, 5];
numbers.sort((a, b) => a - b);
Output:
[1, 2, 5, 10]
This is usually what developers expect.
Descending Order
Example:
numbers.sort((a, b) => b - a);
Output:
[10, 5, 2, 1]
The comparison direction is reversed.
Why a - b Works
Consider:
a = 2
b = 10
Result:
2 - 10
equals:
-8
Negative value:
2 comes before 10
Exactly what we want.
Sorting Strings Properly
String sorting often works without a compare function.
Example:
const names = [
"Alice",
"Bob",
"Charlie"
];
names.sort();
Output:
[
"Alice",
"Bob",
"Charlie"
]
Default behavior was designed primarily for strings.
Case Sensitivity Problems
Example:
[
"apple",
"Banana"
]
Default sort may produce:
[
"Banana",
"apple"
]
because uppercase and lowercase characters have different Unicode values.
Better String Sorting
Use:
names.sort(
(a, b) =>
a.localeCompare(b)
);
Benefits:
- Language awareness
- Better alphabetical ordering
- Improved internationalization
Sorting Objects
Common example:
const users = [
{
name: "John",
age: 32
},
{
name: "Alice",
age: 25
}
];
Sort by age:
users.sort(
(a, b) =>
a.age - b.age
);
Result:
Alice
John
ordered by age.
Sorting Dates
Example:
events.sort(
(a, b) =>
new Date(a.date)
-
new Date(b.date)
);
This produces chronological ordering.
Common Mistake #1
Forgetting the Compare Function
Example:
[5, 20, 100]
.sort();
Output:
[100, 20, 5]
Unexpected but correct according to string sorting rules.
Common Mistake #2
Returning Boolean Values
Incorrect:
array.sort(
(a, b) =>
a > b
);
The compare function should return:
Negative
Zero
Positive
not:
true
false
Results become inconsistent.
Common Mistake #3
Mutating Original Arrays Unexpectedly
Example:
const numbers = [3, 1, 2];
numbers.sort();
sort() modifies the original array.
Result:
numbers
itself changes.
Preserving the Original Array
Use:
const sorted =
[...numbers]
.sort(
(a, b) =>
a - b
);
The original array remains untouched.
Performance Considerations
Modern JavaScript engines implement highly optimized sorting algorithms.
Complexity is typically:
O(n log n)
for average use cases.
Most performance issues arise from:
- Expensive compare functions
- Large object transformations
- Repeated sorting
rather than the sorting algorithm itself.
Real-World Example
Imagine an ecommerce site.
Products:
[
5,
20,
100,
15
]
Default sorting:
products.sort();
Output:
[
100,
15,
20,
5
]
Customers see:
$100
$15
$20
$5
which appears broken.
Correct solution:
products.sort(
(a, b) =>
a - b
);
Now prices display properly.
Why JavaScript Was Designed This Way
Historically:
sort()
was intended primarily for string arrays.
Example:
["dog", "cat", "bird"]
Default string comparison worked naturally.
Numeric sorting required explicit instructions.
The behavior remains for backward compatibility.
Best Practices Checklist
When using Array.sort():
β Use compare functions for numbers
β
Use localeCompare() for strings
β Test sorting edge cases
β
Remember that sort() mutates arrays
β Copy arrays when immutability matters
β Sort objects by specific properties
β Validate date formats before sorting
β Benchmark large datasets when necessary
Common Mistakes to Avoid
Avoid:
β Assuming numeric sorting is automatic
β Returning booleans from compare functions
β Forgetting array mutation behavior
β Ignoring string case sensitivity
β Sorting dates as raw strings
β Using expensive computations inside compare functions
β Trusting default behavior for numeric data
Wrapping Summary
One of the most misunderstood aspects of JavaScript is that Array.sort() does not perform numeric sorting by default. Instead, it converts values to strings and applies lexicographical comparison rules, which often produces unexpected results for numbers such as 1, 10, and 2.
This behavior is not a bug but a design decision rooted in JavaScript's historical focus on string sorting. To achieve reliable numeric ordering, developers must provide a compare function that explicitly defines how values should be compared. The same principle applies when sorting objects, dates, and more complex data structures.
Understanding how compare functions work is essential for writing predictable JavaScript applications. By using appropriate comparison logic, testing edge cases, and remembering that sort() mutates the original array, developers can avoid subtle bugs and ensure their data is ordered exactly as intended.
π€ Share this article
Sign in to saveRelated Articles
Comments (0)
No comments yet. Be the first!