JavaScript Array.sort — Numbers Sorted as Strings
Fix unexpected sort order by supplying a numeric comparator, since the default Array.sort converts elements to strings and compares lexicographically.
Symptoms
- [10, 9, 1] sorts to [1, 10, 9]
- Negative numbers sort in a surprising order
- Sorting objects by a numeric key gives wrong order
Possible causes
- sort is called without a comparator, so elements are compared as strings
- A comparator returns a boolean instead of a negative/zero/positive number
- Sorting mutates the original array in place, changing later reads
Diagnosis steps
Reproduce the string comparison
node -e "console.log([10,9,1].sort())"Interpretation
"1" < "10" < "9" lexicographically explains the output.
Use a numeric comparator
console.log([10,9,1].sort((a, b) => a - b))Interpretation
The comparator returns a number, restoring numeric order.
Root cause
Array.prototype.sort compares by converting elements to strings unless a comparator is given, so "10" precedes "9". A correct comparator must return a negative, zero, or positive number.
Fix
Always pass a comparator for numbers or objects: (a, b) => a - b for ascending, (a, b) => b - a for descending, and compare object fields explicitly. Copy first if the original order must be preserved.
Risk note
sort mutates in place; sorting a shared array can surprise other code. Use toSorted or [...arr].sort.
Verify the fix
Run the sort and confirm ascending numeric order; assert the original array is unchanged when using a copy.
Symptoms
This guide profiles the failure behind “JavaScript Array.sort — Numbers Sorted as Strings”. It walks through the observable symptoms, a reproducible diagnosis path, the underlying root cause, the concrete fix, and how to verify the change and prevent a regression.
Observed symptoms include:
- [10, 9, 1] sorts to [1, 10, 9]
- Negative numbers sort in a surprising order
- Sorting objects by a numeric key gives wrong order
Reproduce and Diagnose
Reproduce the failure in isolation before changing any code. Work through the checks below in order; each one narrows the search space.
Step 1: Reproduce the string comparison
node -e "console.log([10,9,1].sort())"
“1” < “10” < “9” lexicographically explains the output.
Step 2: Use a numeric comparator
console.log([10,9,1].sort((a, b) => a - b))
The comparator returns a number, restoring numeric order.
Likely contributors, in order of frequency:
- sort is called without a comparator, so elements are compared as strings
- A comparator returns a boolean instead of a negative/zero/positive number
- Sorting mutates the original array in place, changing later reads
Root Cause
Array.prototype.sort compares by converting elements to strings unless a comparator is given, so “10” precedes “9”. A correct comparator must return a negative, zero, or positive number.
The Fix
Always pass a comparator for numbers or objects: (a, b) => a - b for ascending, (a, b) => b - a for descending, and compare object fields explicitly. Copy first if the original order must be preserved.
Watch out: sort mutates in place; sorting a shared array can surprise other code. Use toSorted or […arr].sort.
const nums = [10, 9, 1, 100];
// ❌ lexicographic -> [1, 10, 100, 9]
// nums.sort();
// ✅ numeric ascending
console.log([...nums].sort((a, b) => a - b)); // [1, 9, 10, 100]
// objects by key, without mutating the source
const sorted = [...items].sort((a, b) => a.score - b.score);
Verify and Prevent
Run the sort and confirm ascending numeric order; assert the original array is unchanged when using a copy.
Once verified, add a regression test that reproduces the original symptom so the failure cannot silently return.
This class of failure is usually environmental rather than pure logic, so capture the exact command, the environment variables, and the working directory in your bug report. Reproduce it in the smallest possible script, then widen the scope only once the minimal case is understood. A short written checklist for this failure mode saves hours the next time it appears.