Sorting Arrays
sort puts an array in order, but it compares items as text unless you give it a compare function, and it changes the array itself, so toSorted is the way to get a sorted copy and keep the original.
Apprentice · JavaScript · Updated
After this lesson you can
- Sort numbers and objects with a compare function, and explain why sort() alone puts 100 before 9
- Sort a copy with toSorted, leaving the original array in its order
- Choose map, filter, reduce, sort or a chain of them for a job
Key takeaways
- sort with no compare function sorts items as text, so [10, 9, 100].sort() gives [10, 100, 9].
- A compare function gets two items, a and b, and returns a negative number to put a first or a positive number to put b first.
- (a, b) => a - b sorts numbers from lowest to highest, and (a, b) => b - a from highest to lowest.
- sort changes the array you call it on. toSorted gives back a sorted copy and leaves the original in its order.
- Pick the method by the job. map changes every item, filter keeps some, reduce boils them down to one value, sort or toSorted puts them in order.
"Price: low to high." "Newest first." "Top scores." Every shop, inbox and game puts lists in order, and the people using them expect it to be right: a leaderboard that puts 9 points above 100 points loses its players' trust fast. JavaScript arrays have a sort method, and it comes with one famous surprise. This lesson shows you the surprise, the fix, and how to sort without losing the order you started with.
The examples use the Comet Dash high-score list: gamer tags, scores, levels and the date each game was played.
Putting things in order with sort
Think of putting a shelf of board games in alphabetical order. sort puts an array of words like these in order:
const tags = ["NovaFox", "LunaByte", "PixelPanda", "CaptainCrumb"];
tags.sort();
console.log(tags);
[ 'CaptainCrumb', 'LunaByte', 'NovaFox', 'PixelPanda' ]
(It compares character codes, so every capital letter comes before every small one: ['banana', 'Cherry'].sort() puts Cherry first. localeCompare, below, sorts the way a person would.)
Notice that the line tags.sort(); doesn't save a result anywhere, and tags is still sorted afterward. sort reorders the array in place: it changes the array you call it on. Keep that in mind; it comes back later in this lesson.
The surprise: numbers sorted as text
Now try it with scores:
const scores = [10, 9, 100];
scores.sort();
console.log(scores);
[ 10, 100, 9 ]
That's not a typo. With nothing to go on, sort turns every item into text and puts the text in order character by character, as text. In a dictionary, every word starting with "1" comes before any word starting with "9", so "10" and "100" both land before "9". It's the same reason a folder of files named track1, track10 and track9 can show up in that order.
A compare function: which one goes first?
To sort numbers as numbers, you give sort a compare function: a callback that gets two items, a and b, and answers "which goes first?" with a number.
- A negative number means
agoes first. - A positive number means
bgoes first. - 0 means they're tied, so they keep the order they had.
Subtracting gives exactly that kind of answer. a - b (read "a minus b") is negative when a is smaller, so the small numbers go first:
const scores = [10, 9, 100];
scores.sort((a, b) => a - b);
console.log(scores);
scores.sort((a, b) => b - a);
console.log(scores);
[ 9, 10, 100 ]
[ 100, 10, 9 ]
(a, b) => a - b sorts lowest to highest (in textbooks, ascending). (a, b) => b - a sorts highest to lowest (descending), which is what a leaderboard wants.
Sorting records by score, by name or by date
A leaderboard sorts player objects, not plain numbers, so the compare function compares one property of each:
const highScores = [
{ tag: "PixelPanda", score: 8200, date: "2026-10-03" },
{ tag: "NovaFox", score: 12450, date: "2026-10-05" },
{ tag: "LunaByte", score: 9900, date: "2026-10-07" },
];
highScores.sort((a, b) => b.score - a.score);
console.log(highScores.map((player) => player.tag));
highScores.sort((a, b) => a.tag.localeCompare(b.tag));
console.log(highScores.map((player) => player.tag));
highScores.sort((a, b) => b.date.localeCompare(a.date));
console.log(highScores.map((player) => player.date));
[ 'NovaFox', 'LunaByte', 'PixelPanda' ]
[ 'LunaByte', 'NovaFox', 'PixelPanda' ]
[ '2026-10-07', '2026-10-05', '2026-10-03' ]
You can't subtract text, so for tags, a.tag.localeCompare(b.tag) does the comparing: it returns a negative number, a positive number or 0, exactly what sort needs, and it puts words in A-to-Z order the way a person would. Dates written year-month-day, like "2026-10-07", sort correctly as text too, so b.date.localeCompare(a.date) puts the newest game first.
Try it. In a file named sorting.js, add CaptainCrumb (3100 points, played "2026-10-05") to the list, and sort the players by score from lowest to highest. Run it with node sorting.js.
sort changes the original; toSorted makes a copy
Because sort reorders the array itself, sorting a list for one screen changes it for every part of the program that uses it. Say the game shows players in the order they signed up, and the leaderboard sorts the same array:
const signUps = [450, 8200, 3100];
const sorted = signUps.sort((a, b) => b - a);
console.log(signUps, sorted === signUps);
const signUpsAgain = [450, 8200, 3100];
const ranked = signUpsAgain.toSorted((a, b) => b - a);
console.log(signUpsAgain, ranked);
[ 8200, 3100, 450 ] true
[ 450, 8200, 3100 ] [ 8200, 3100, 450 ]
sorted === signUps (read "strictly equal") is true: they're the same array, and the sign-up order is gone. toSorted takes the same compare function, but gives back a new, sorted array and leaves the original alone. Making a new value instead of changing the old one is called immutability, and it saves you from a whole family of "who changed my list?" bugs. toSorted arrived in JavaScript in 2023; every current browser and Node.js 20 or later have it. Older code makes the copy first, with spread: [...signUps].sort((a, b) => b - a).
Putting it together: a top-3 leaderboard
Now every piece of this chapter can work in one chain. slice(0, 3) copies the items from position 0 up to, but not including, position 3: the first three.
const highScores = [
{ tag: "PixelPanda", score: 8200, level: 7 },
{ tag: "NovaFox", score: 12450, level: 9 },
{ tag: "CaptainCrumb", score: 3100, level: 3 },
{ tag: "LunaByte", score: 9900, level: 8 },
{ tag: "TurboSnail", score: 450, level: 1 },
];
const topThree = highScores
.toSorted((a, b) => b.score - a.score)
.slice(0, 3)
.map((player, index) => `${index + 1}. ${player.tag}: ${player.score}`);
topThree.forEach((line) => console.log(line));
1. NovaFox: 12450
2. LunaByte: 9900
3. PixelPanda: 8200
When you face a new list, ask what the job is, and the method follows:
| The job | The method | It gives back |
|---|---|---|
| Change every item (scores to display lines) | map |
A new array, the same length |
| Keep only some items (players at level 5 or higher) | filter |
A new array, the same length or shorter |
| Boil the list down to one answer (the total score) | reduce |
One value |
| Put the items in order (highest score first) | toSorted, or sort to change the original |
A sorted array |
| Several of these in a row (the top 3, as lines) | A chain, such as toSorted, then slice, then map |
Whatever the last step gives |
Check yourself
2 questions to try on the lesson page.
So what
Putting a list in order sounds like the plainest job there is, and it's the one with the famous trap. Give sort a compare function, sort a copy with toSorted, and your leaderboards, price lists and inboxes come out the way people expect.
Key terms
- sort()
- Compare Function
- toSorted()
- slice()
- Immutability