Topics

Jump to a topic.

Search questions

Find a question by title, tag, or summary.

How does Array.prototype.sort work?

Questions in this chapter (7)
  • Draft
  • medium
  • Askedvery often
  • Verified October 2026

Answer

sort reorders the array in place and returns the same array. For a copy, use toSorted.

Default order. With no comparator, every element is converted with ToString and the strings are compared by UTF-16 code units. That is why numbers sort as text ([10, 9, 1, 100] becomes [1, 10, 100, 9]) and why 'B' sorts before 'a'.

Comparator. (a, b) => number returns a negative number to put a first, a positive number to put b first, and 0 to treat them as equal. For numbers, a - b ascending and b - a descending is the usual idiom (safe for finite numbers). The spec converts the result with ToNumber, and treats NaN as 0. Returning a boolean (a > b) is wrong: false and true become 0 and 1, and a comparator must be able to say "less than". The result depends on the algorithm, so it's implementation-defined.

Stability. Since ES2019 the spec requires a stable sort: items the comparator calls equal keep their original relative order. That is what makes "sort by last name, then by country" work with two passes.

Consistent comparator. If the comparator is not consistent (same answer for the same pair, transitive, symmetric), the resulting order is implementation-defined. A random comparator like () => Math.random() - 0.5 is therefore not a correct shuffle.

undefined and holes. undefined values are never passed to the comparator. They are moved to the end, followed by holes (the spec collects only existing indices, sorts them, writes them back and deletes the remaining indices). toSorted instead reads holes as undefined.

Text. Use a.localeCompare(b) or an Intl.Collator for human-language order; code-unit order puts 'é' after 'z'.

The algorithm is implementation-defined: the spec fixes the result, not the order of comparator calls.

Example

console.log([10, 9, 1, 100].sort());                // → [ 1, 10, 100, 9 ]
console.log([10, 9, 1, 100].sort((a, b) => a - b)); // → [ 1, 9, 10, 100 ]

const people = [
  { name: 'Ana', age: 30 }, { name: 'Bo', age: 25 },
  { name: 'Cy', age: 30 }, { name: 'Di', age: 25 },
];
const byAge = people.toSorted((a, b) => a.age - b.age);
console.log(byAge.map((p) => p.name).join()); // → Bo,Di,Ana,Cy  (equal ages keep input order)

console.log(['b', undefined, 'a', , 'c'].sort());
// → [ 'a', 'b', 'c', undefined, <1 empty item> ]

console.log(['é', 'z', 'a'].sort());                             // → [ 'a', 'z', 'é' ]
console.log(['é', 'z', 'a'].sort((a, b) => a.localeCompare(b))); // → [ 'a', 'é', 'z' ]

// Boolean comparator: result is implementation-defined; in Node 24 (V8) it sorts nothing
console.log([3, 1, 2].sort((a, b) => a > b)); // → [ 3, 1, 2 ]

Pitfalls

  • Calling sort() on numbers without a comparator.
  • Returning a boolean from the comparator. It looks right on a few inputs in some engines and breaks in others.
  • Using a - b where the values can be NaN or Infinity. Infinity - Infinity is NaN, which is treated as equal. For strings use localeCompare or explicit < / > checks.
  • Sorting a shared array in place (props.items.sort(...)) when the data belongs to someone else. Use toSorted.

Follow-ups

Why did older code add an index tiebreaker to get a stable sort?
Before ES2019 the spec did not require stability, and some engines used an unstable sort for larger arrays (Chrome 70 shipped a stable sort, and V8 uses TimSort). Today equal keys keep their order, so a single sort with a tie-breaker comparator is enough.
How do you sort by an expensive derived key?
Compute the key once per element (map to { key, value } pairs), sort the pairs, then map back. A comparator that recomputes the key runs O(n log n) times.
Does sort work on array-likes?
Yes. It is generic: Array.prototype.sort.call({ 0: 'b', 1: 'a', length: 2 }) sorts the object in place and returns it.

Exercises

Predict the output

const items = [
  { id: 1, tag: 'b' }, { id: 2, tag: 'a' },
  { id: 3, tag: 'b' }, { id: 4, tag: 'a' },
];
items.sort((x, y) => (x.tag < y.tag ? -1 : x.tag > y.tag ? 1 : 0));
console.log(items.map((i) => i.id).join());
console.log([5, 25, 100, 1].sort().join());