Sorting Arrays

Sorting looks simple but has two famous traps in JavaScript: by default, sort compares items as strings, and it changes the original array. Once you know how compare functions work, you can sort numbers, dates, objects by any property, and text in any language.

The default sort

JS default.js
const names = ["Mia", "ada", "Zoe", "Ben"];
names.sort();
console.log(names);

const numbers = [100, 25, 3, 1000];
numbers.sort();
console.log(numbers);
Output
[ 'Ben', 'Mia', 'Zoe', 'ada' ]
[ 100, 1000, 25, 3 ]

Without arguments, sort converts every item to a string and orders by Unicode code points. That puts uppercase before lowercase and sorts numbers "alphabetically": "1000" comes before "25" because "1" comes before "2".

Compare functions

Pass a function that takes two items, a and b, and returns:

  • a negative number if a should come first,
  • a positive number if b should come first,
  • 0 if their order doesn't matter.
JS numbers.js
const numbers = [100, 25, 3, 1000];

console.log([...numbers].sort((a, b) => a - b));
console.log([...numbers].sort((a, b) => b - a));
Output
[ 3, 25, 100, 1000 ]
[ 1000, 100, 25, 3 ]

a - b is negative when a is smaller, so smaller numbers go first (ascending). b - a reverses it.

sort changes the original

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

console.log(original);
console.log(sorted === original);

const safe = [3, 1, 2];
const copy = safe.toSorted((a, b) => a - b);
console.log(safe, copy);
Output
[ 1, 2, 3 ]
true
[ 3, 1, 2 ] [ 1, 2, 3 ]

sort sorts in place and returns the same array. To keep the original, use toSorted or copy first with [...arr].sort(…).

Sorting objects by a property

JS objects.js
const products = [
  { name: "Desk", price: 120, rating: 4.1 },
  { name: "Lamp", price: 30, rating: 4.8 },
  { name: "Chair", price: 75, rating: 4.8 },
];

const cheapest = products.toSorted((a, b) => a.price - b.price);
console.log(cheapest.map(p => p.name));

const best = products.toSorted((a, b) => b.rating - a.rating || a.price - b.price);
console.log(best.map(p => p.name));
Output
[ 'Lamp', 'Chair', 'Desk' ]
[ 'Lamp', 'Chair', 'Desk' ]

The second sort orders by rating (highest first), and breaks ties by price: when ratings are equal, b.rating - a.rating is 0 (falsy), so || falls through to the price comparison.

Sorting text properly

localeCompare compares strings the way a human would expect, ignoring case differences and handling accents:

JS locale.js
const words = ["zebra", "Apple", "éclair", "banana"];

console.log(words.toSorted());
console.log(words.toSorted((a, b) => a.localeCompare(b)));

const files = ["file10", "file2", "file1"];
console.log(files.toSorted((a, b) => a.localeCompare(b, undefined, { numeric: true })));
Output
[ 'Apple', 'banana', 'zebra', 'éclair' ]
[ 'Apple', 'banana', 'éclair', 'zebra' ]
[ 'file1', 'file2', 'file10' ]

The numeric: true option gives "natural" ordering for strings that contain numbers. For sorting large lists, creating one Intl.Collator and reusing its compare method is faster.

Sorting dates

JS dates.js
const events = [
  { title: "Launch", date: new Date("2026-11-01") },
  { title: "Kickoff", date: new Date("2026-03-15") },
  { title: "Review", date: new Date("2026-07-20") },
];

events.sort((a, b) => a.date - b.date);
console.log(events.map(e => e.title));
Output
[ 'Kickoff', 'Review', 'Launch' ]

Subtracting two dates gives the difference in milliseconds, which works directly as a compare result. ISO date strings ("2026-03-15") can also be compared with localeCompare, because their text order matches date order.

Reverse and shuffle

JS shuffle.js
const deck = [1, 2, 3, 4, 5];

function shuffle(array) {
  const result = [...array];
  for (let i = result.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [result[i], result[j]] = [result[j], result[i]];
  }
  return result;
}

const shuffled = shuffle(deck);
console.log(shuffled.length, shuffled.toSorted((a, b) => a - b).join() === deck.join());
Output
5 true

This is the Fisher–Yates shuffle, which gives every order an equal chance. The tempting shortcut sort(() => Math.random() - 0.5) is biased and shouldn't be used.

Try it yourself

Try it yourself
Console output