albert.walickihire me
← all solutions
javascript

How to sort an array of objects by property in JavaScript?

Sort an array of objects by a string, number or date property with a compare function, sort by several keys, and avoid the default sort and mutation traps.

Pass a compare function to sort(). For strings, use localeCompare(); for numbers, subtract one value from the other.

const users = [
  { name: 'Zoe', age: 31 },
  { name: 'adam', age: 25 },
  { name: 'Émile', age: 40 },
];

users.sort((a, b) => a.name.localeCompare(b.name));
// adam, Émile, Zoe

users.sort((a, b) => a.age - b.age);
// adam (25), Zoe (31), Émile (40)

The compare function gets two items and returns a number. Negative puts a first, positive puts b first, and 0 keeps their current order. sort() is stable, so items that compare equal stay in the order they were in.

localeCompare() sorts the way people expect. A plain < comparison compares character codes, so it would put Zoe before adam (uppercase letters come first) and Émile after both.

Descending order

Swap a and b:

users.sort((a, b) => b.age - a.age);
users.sort((a, b) => b.name.localeCompare(a.name));

Sorting by several properties

Chain the comparisons with ||. When the first one returns 0 (a tie), 0 is falsy, so the next comparison decides:

// by role A to Z, then oldest first within each role
users.sort(
  (a, b) =>
    a.role.localeCompare(b.role) ||
    b.age - a.age
);

Dates

Date objects can be subtracted, because they turn into timestamps:

posts.sort((a, b) => b.date - a.date); // newest first

ISO date strings like '2026-09-16T12:00:00Z' sort correctly as plain strings, as long as they all use the same format and time zone, so a.date.localeCompare(b.date) works. For other formats, convert first: new Date(b.date) - new Date(a.date).

sort() changes the original array

sort() sorts in place and returns the same array, so const sorted = users.sort(...) isn't a copy. That matters with React state or props, and whenever other code uses the array. Use toSorted() (ES2023) or copy first:

const byAge = users.toSorted((a, b) => a.age - b.age);

// the same, for older environments
const byAgeCopy = [...users].sort((a, b) => a.age - b.age);

The default sort compares strings

Without a compare function, sort() turns every item into a string and compares character codes. That's why numbers come out in a strange order, and why objects don't get sorted at all (each one becomes "[object Object]"):

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

Don't return a boolean

// wrong
[3, 1, 2].sort((a, b) => a > b); // [3, 1, 2] in Chrome and Node

true becomes 1 and false becomes 0, so the function never returns a negative number. The engine treats many pairs as equal, and the result depends on the browser's sorting algorithm. It can even look right in a quick test. Always return a number.

Natural and case-insensitive order

localeCompare() puts item 10 before item 2, because it compares character by character. For natural order, use Intl.Collator with numeric: true. sensitivity: 'base' also makes a, A and á compare as equal:

const collator = new Intl.Collator(undefined, {
  numeric: true,
  sensitivity: 'base',
});

const files = [
  { name: 'item 10' },
  { name: 'Item 2' },
  { name: 'item 1' },
];

files.sort((a, b) => collator.compare(a.name, b.name));
// item 1, Item 2, item 10

Creating one collator and reusing it is faster than passing the same options to localeCompare() for every comparison, which matters for large arrays.

If the property can be missing, a.name.localeCompare() throws a TypeError. Fall back to an empty string: (a.name ?? '').localeCompare(b.name ?? '').

more solutions
work with me

Got something that needs building?

Frontend builds, full-stack features in Django, design-system work. Available for work.

See my workGet in touch