JavaScript example

How to sort an array of objects by property in JavaScript

6 min read Updated Sep 2026 Runs in an isolated runtime
Quick answer

Pass sort() a compare function that reads the property: arr.sort((a, b) => a.price - b.price) for numbers, arr.sort((a, b) => a.name.localeCompare(b.name)) for strings. Swap a and b for descending, and use toSorted() instead of sort() to get a new array without touching the original.

Array.prototype.sort has no "sort by key" option. Instead it calls your compare function on pairs of elements and reads the sign of what comes back: negative puts a first, positive puts b first, and zero means they tie. Every pattern on this page is a different way to produce that number, whether the property holds a number, a string or a date. With no compare function at all, sort() turns every element into a string first, which is why [10, 9, 1] does not sort the way you expect. Each example runs on this page: hit Run, then edit the code and run it again.

1Sort by a number propertyRecommended

For numeric properties, subtract: a.price - b.price is negative when a is cheaper, positive when it costs more, and zero when they match. That is exactly the contract sort() wants. The second half of the snippet shows why you always pass a compare function, even for a plain array of numbers.

sort-numbers.js

Output

Prints [ 'Mouse $25', 'Keyboard $49', 'Webcam $70', 'Monitor $189' ], then [ 1, 10, 100, 25, 9 ] for the bare sort() and [ 1, 9, 10, 25, 100 ] with (a, b) => a - b. The default sort compares "100" and "9" character by character, and "1" comes before "9". Subtraction works for any finite numbers, negatives and decimals included. It does not work for BigInt values: the result is a BigInt, and sort() throws TypeError: Cannot convert a BigInt value to a number. Compare those with a < b ? -1 : a > b ? 1 : 0 instead.

2Sort by a string property with localeCompare

You cannot subtract strings, so use a.name.localeCompare(b.name). It returns a negative number, zero or a positive number, and it orders text the way a dictionary does: accents sit next to their base letter and capitals do not jump the queue. The </> version many older answers use compares raw UTF-16 code units instead, and the difference shows up as soon as the data has a lowercase or accented name.

sort-strings.js

Output

Prints [ 'Ana', 'Bea', 'émile', 'zoe' ] with localeCompare but [ 'Ana', 'Bea', 'zoe', 'émile' ] with </>: lowercase z sorts after every capital, and é after every plain letter. The collator line prints [ 'v1.txt', 'v2.txt', 'v10.txt' ], the natural order people expect for version numbers and file names. With no locale argument, localeCompare uses the runtime's default (en-US here), so pass one (a.first.localeCompare(b.first, "en")) when the order must match on every machine. For large arrays, a single Intl.Collator is faster than calling localeCompare with options on every comparison.

3Sort by a date property

Dates reduce to the number case. Date.parse() turns an ISO 8601 string into milliseconds since the epoch, and getTime() does the same for a Date object. Subtract those numbers and you have a chronological sort. To sort newest first, subtract the other way round.

sort-dates.js

Output

Prints [ 102, 103, 101 ] (oldest order first), then launch 2026-06-01, beta 2026-04-10, alpha 2026-02-01. You will also see new Date(b.date) - new Date(a.date); it works in JavaScript because subtraction calls valueOf(), but TypeScript rejects arithmetic on Date, so getTime() is the portable spelling. If every value is an ISO string in the same format and the same offset (all Z, say), the strings already sort chronologically with </>; mixed offsets or formats break that. An unparseable date becomes NaN, which sort() treats as a tie, so filter or validate bad dates before sorting.

4Descending order and tie-breaks

Descending is the same comparison with a and b swapped: b.score - a.score. For a second key, chain comparisons with ||. A comparison that returns 0 is falsy, so || moves on to the next one only when the first key ties. Since ES2019 sort() is also guaranteed stable: elements that compare equal keep the order they already had.

sort-multi-key.js

Output

The first block prints 90 alice, 90 dana, 70 bob, 70 carol: highest score first, alphabetical inside each score. The second block prints blue 90 alice, blue 70 bob, red 90 dana, red 70 carol, because the stable team sort kept the score order within each team. Prefer the one-pass || chain: it states the whole ordering in one place. Sorting ascending and calling .reverse() also gives descending order, but it costs a second pass and flips the order of tied elements too.

5toSorted() vs sort(): keep the original

sort() reorders the array in place and returns that same array, so any other code holding a reference sees the new order. toSorted() (ES2023, Node 20+ and every current browser) takes the same compare function but returns a new array and leaves the original untouched.

to-sorted.js

Output

Prints [ 'Mouse', 'Monitor', 'Keyboard' ] (the original, unchanged), [ 'Mouse', 'Keyboard', 'Monitor' ], true, true, then [ 'Monitor', 'Keyboard', 'Mouse' ]. The copy is shallow: the new array holds the same objects, so editing cheapestFirst[0].price also changes the object in cart. On runtimes older than ES2023, copy first with [...cart].sort(cmp) or cart.slice().sort(cmp). The trap to avoid is const sorted = cart.sort(cmp) in the belief that it made a copy; it did not.

6Which should you use?

ComparatorProperty typeBest for
(a, b) => a.price - b.priceNumbersPrices, counts, ids, scores
a.name.localeCompare(b.name)StringsNames, titles, any human-facing text
collator.compare(a.name, b.name)StringsLarge arrays, numeric-aware order (v2 before v10)
Date.parse(a.at) - Date.parse(b.at)DatesISO timestamps; getTime() for Date objects
b.score - a.score || a.name.localeCompare(b.name)Several keysLeaderboards, ties broken on purpose
arr.toSorted(cmp)AnyA sorted copy; the original stays as it was

Frequently asked questions

Why does sort() put 10 before 9 in JavaScript?

With no compare function, sort() converts every element to a string and compares the strings, so "10" comes before "9" because "1" comes before "9". [10, 9, 1, 100, 25].sort() gives [ 1, 10, 100, 25, 9 ]. Pass a compare function to sort numerically: nums.sort((a, b) => a - b).

How do I sort an array of objects in descending order?

Swap a and b in the comparison: arr.sort((a, b) => b.price - a.price) for numbers, arr.sort((a, b) => b.name.localeCompare(a.name)) for strings. That is one pass. Sorting ascending and then calling .reverse() also works, but it takes a second pass and flips the order of tied elements.

Can the compare function return true or false?

No. A comparator like (a, b) => a.price > b.price returns true or false, which sort() reads as 1 or 0. It never returns a negative number, so the engine is told that half the pairs are equal, and the result can come out unsorted. Return a number: a.price - b.price, or a < b ? -1 : a > b ? 1 : 0.

How do I sort by a property whose name is in a variable?

Use bracket access inside the comparator: arr.sort((a, b) => a[key] - b[key]) for numbers, or a[key].localeCompare(b[key]) for strings. A small factory makes it reusable: const byKey = (key) => (a, b) => typeof a[key] === "string" ? a[key].localeCompare(b[key]) : a[key] - b[key]; then arr.sort(byKey("name")).

Is Array.prototype.sort stable?

Yes. Since ES2019 the specification requires sort() and toSorted() to be stable, so elements that compare equal keep their original relative order. Every current engine, including V8 in Node and Chrome, follows it. That is what makes sorting twice, least important key first, a valid way to sort by several keys.

Does sort() modify the original array?

Yes. sort() reorders the array in place and returns the same array, so const sorted = arr.sort(cmp) leaves sorted === arr. Use arr.toSorted(cmp) (ES2023) for a new sorted array, or [...arr].sort(cmp) on older runtimes. Both copies are shallow: the objects inside are shared.

Run it yourself

Open any of these in the full JavaScript editor: tweak, run, and share.

JavaScript playground