DEV Community

Mohamed Idris
Mohamed Idris

Posted on

JavaScript .sort(), "Why b - a, Not a - b"

The thing that always trips people up

You see this in someone's code:

numbers.sort((a, b) => b - a);
Enter fullscreen mode Exit fullscreen mode

And you think:

  • "Why subtract?"
  • "Why b - a and not a - b?"
  • "What does this even return?"

Three minutes from now you will never forget it.

How .sort() actually works

Array.sort() reorders an array. To do that, it needs to compare two items at a time and decide which one goes first. You teach it how by giving it a compare function.

The contract is one rule:

compareFn(a, b) returns:
  a NEGATIVE number  →  a comes first
  a POSITIVE number  →  b comes first
  ZERO               →  leave them in their current order
Enter fullscreen mode Exit fullscreen mode

That is it. The function returns a number; the sign is the only thing that matters.

The cute mental model

Think of the compare function as a referee judging a match between two contestants, a and b. After the match, the referee gives a score. The sign of the score is the verdict:

  • Negative scorea wins → a moves to the front.
  • Positive scoreb wins → b moves to the front.
  • Zero → tie → nothing moves.

That's the whole rule. The function returns a number; the sign decides who gets the front spot.

An important caveat about the contract

A good comparator must be consistent: if you swap the arguments, you must get the opposite sign. That is, compareFn(a, b) and compareFn(b, a) must disagree (one positive, one negative — or both zero if equal).

What you should not do is return a constant like -1 regardless of arguments:

[10, 2].sort((a, b) => -1);   // ❌ tells the engine "a first" no matter what a is
Enter fullscreen mode Exit fullscreen mode

That makes no sense as a comparison ("a is always less than b, even when a and b are swapped"). The engine doesn't error — it just trusts you — and the result becomes a quirk of which order the engine happened to call your function. Don't write comparators like that. Always make the answer depend on a and b.

The good news: subtraction is naturally consistent. a - b and b - a are mirror images, so it works no matter which order the engine calls the comparator. That is why the next section is about subtraction.

Why subtraction?

Subtraction is a quick trick to produce that signed number.

a - b is negative  ⇒  a is smaller  ⇒  a comes first
a - b is positive  ⇒  a is bigger   ⇒  b comes first
a - b is zero      ⇒  equal         ⇒  keep order
Enter fullscreen mode Exit fullscreen mode

If you want ascending (small to big), use a - b.
If you want descending (big to small), flip it: b - a.

Rule of thumb: the variable on the left of the subtraction is the one that ends up at the small end of the sorted array.

  • a - ba is on the left → small values land at the start (ascending).
  • b - ab is on the left → small values land at the end (descending).

A worked example

Sort [3, 1, 4, 1, 5, 9, 2, 6] in ascending order:

[3, 1, 4, 1, 5, 9, 2, 6].sort((a, b) => a - b);
// [1, 1, 2, 3, 4, 5, 6, 9]
Enter fullscreen mode Exit fullscreen mode

Descending order:

[3, 1, 4, 1, 5, 9, 2, 6].sort((a, b) => b - a);
// [9, 6, 5, 4, 3, 2, 1, 1]
Enter fullscreen mode Exit fullscreen mode

Same numbers, opposite directions, one character changed in the compare function.

What happens if you forget the compare function

[10, 2, 100, 4].sort();
// [10, 100, 2, 4]   ← what?!
Enter fullscreen mode Exit fullscreen mode

.sort() with no arguments converts every element to a string and sorts alphabetically. "10" comes before "2" because the character '1' is alphabetically smaller than '2'. Same for "100" — '1' < '2', so "100" appears before "2" even though numerically 100 is much bigger.

Lesson: always pass a compare function for numbers.

Sorting objects by a field

This is where the trick really earns its keep. Imagine an array of workers:

const workers = [
  { name: "Alice", shifts: 3 },
  { name: "Bob",   shifts: 9 },
  { name: "Carol", shifts: 5 },
];
Enter fullscreen mode Exit fullscreen mode

Most shifts first (descending):

workers.sort((a, b) => b.shifts - a.shifts);
// [
//   { name: "Bob",   shifts: 9 },
//   { name: "Carol", shifts: 5 },
//   { name: "Alice", shifts: 3 },
// ]
Enter fullscreen mode Exit fullscreen mode

Least shifts first (ascending):

workers.sort((a, b) => a.shifts - b.shifts);
// [
//   { name: "Alice", shifts: 3 },
//   { name: "Carol", shifts: 5 },
//   { name: "Bob",   shifts: 9 },
// ]
Enter fullscreen mode Exit fullscreen mode

Notice: you only change the order inside the subtraction. The structure stays identical.

Sorting strings (where subtraction won't work)

Subtraction is for numbers. Strings need localeCompare:

const names = ["Carol", "Alice", "Bob"];
names.sort((a, b) => a.localeCompare(b));
// ["Alice", "Bob", "Carol"]
Enter fullscreen mode Exit fullscreen mode

localeCompare returns negative / zero / positive following the same contract.

One little gotcha — sort mutates the original

const original = [3, 1, 2];
const sorted = original.sort((a, b) => a - b);

original;  // [1, 2, 3]   ← changed!
sorted;    // [1, 2, 3]   ← same reference
Enter fullscreen mode Exit fullscreen mode

If you want a sorted copy without touching the original, use toSorted (modern browsers/Node 20+) or copy first:

const sorted = [...original].sort((a, b) => a - b);
// original stays [3, 1, 2]
Enter fullscreen mode Exit fullscreen mode

Cheat sheet to pin on your wall

ascending  (small to big):  (a, b) => a - b
descending (big to small):  (a, b) => b - a

ascending by field:         (a, b) => a.field - b.field
descending by field:        (a, b) => b.field - a.field

strings ascending:          (a, b) => a.localeCompare(b)
strings descending:         (a, b) => b.localeCompare(a)
Enter fullscreen mode Exit fullscreen mode

TL;DR

.sort() calls your compare function on pairs of elements. The function returns a signed number:

  • Negative → a comes first.
  • Positive → b comes first.
  • Zero → leave them as-is.

Subtraction is a tiny trick to produce that signed number, and the order of the operands controls the direction.

a - b = small first (ascending).
b - a = big first (descending).

Now you will never wonder again.

Top comments (0)