Q1When the compare function returns a negative value, which element does sort put first?
Sorting and Searching — sort / find / some / every
Learn sort, which orders an array by a compare function, toSorted, which keeps the original, find and findIndex, which return one match, and some and every.
If you sort a list by writing a for loop that swaps elements, you have to manage both the comparisons and the swaps yourself, every single time. Searching for the one item that matches a condition leads to the same kind of code: a for loop with a break to exit as soon as the item is found.
This article covers sort, which reorders an array, and find, which searches for an element that matches a condition.
Specifying the Order With a Function — sort and Compare Functions
Say you want to display a product list sorted from cheapest to most expensive. An array method handles the sorting, but writing just products.sort() won't sort by price, because you haven't told it what to compare.
You pass sort (a method that reorders an array's elements) a compare function (a function that takes two elements and returns a number). If the two elements it receives are called a and b, a negative return value puts a first and a positive one puts b first. Return a.price - b.price, and you get prices in ascending order.
const products = [
{ name: "Wireless Mouse", price: 2980 },
{ name: "Power Bank", price: 4380 },
{ name: "USB-C Cable", price: 980 },
{ name: "Mouse Pad", price: 2980 },
];
// If the subtraction is negative, a comes first; if positive, b comes first
products.sort((a, b) => a.price - b.price);
console.log(products.map((item) => item.price).join(", ")); // 980, 2980, 2980, 4380
// Check the order of the two items with the same price
console.log(products[1].name, products[2].name); // Wireless Mouse Mouse Pad
// Flip the subtraction and you get descending order
products.sort((a, b) => b.price - a.price);
console.log(products[0].name); // Power Bank
sort only looks at the sign of the number returned, so -2000 and -1 are treated the same. To also control the order of items with the same price, have the compare function compare another value when the difference is 0.
Calling sort Without Arguments — Default Comparison and toSorted
Say you want to sort an array of time-on-page values, in seconds, from shortest to longest. If you call sort() without a compare function, the result isn't in numeric order: 1180 seconds comes before 25 seconds.
Without a compare function, sort first converts the elements to strings and then compares them character by character. 1180 is treated as "1180" and 25 as "25", so the order is decided by the leading 1 and 2. sort reorders the array it's called on in place, so when you also want to keep the original order, use toSorted (a method that returns a new, sorted array).
const viewSeconds = [1180, 980, 25, 4380];
// Without a compare function, they're compared as strings
console.log(viewSeconds.sort().join(", ")); // 1180, 25, 4380, 980
// Pass a compare function and they're sorted numerically
console.log(viewSeconds.sort((a, b) => a - b).join(", ")); // 25, 980, 1180, 4380
// toSorted returns a new array and leaves the original unchanged
const stayTimes = [1180, 980, 25, 4380];
console.log(stayTimes.toSorted((a, b) => a - b).join(", ")); // 25, 980, 1180, 4380
console.log(stayTimes.join(", ")); // 1180, 980, 25, 4380
Once they're strings, "1180" will always come before "25", so pass a compare function when you sort an array of numbers. The second sort in the code above sorts viewSeconds again, after the first call already rearranged it.
sort returns in the top row is the rearranged viewSeconds itself. Only toSorted creates a new array.Older Browsers Don't Have toSorted
toSorted was added to the spec in ES2023, and in older browsers that don't support it, the line that calls it throws a TypeError. To make it work in those environments too, call sort on a copy made with spread syntax, as in [...stayTimes].sort((a, b) => a - b).
Getting the One Matching Item — find and findIndex
Say you want to get one reservation by its reservation code. You could use filter, but it returns an array, so you'd have to add [0] to read the first item — and if no reservation matches, you get an empty array back.
find (a method that returns the first element that matches a condition) returns the element itself: the first one for which the callback returns true. When you want its position instead, use findIndex (a method that returns the position of the first element that matches a condition).
const reservations = [
{ code: "RSV-201", room: "Meeting Room A" },
{ code: "RSV-202", room: "Meeting Room B" },
{ code: "RSV-203", room: "Meeting Room C" },
];
// Returns the first element for which the callback returned true
const target = reservations.find((item) => item.code === "RSV-202");
console.log(target.room); // Meeting Room B
// Use findIndex when you want the position
console.log(reservations.findIndex((item) => item.code === "RSV-202")); // 1
// When no reservation matches
console.log(reservations.find((item) => item.code === "RSV-999")); // undefined
console.log(reservations.findIndex((item) => item.code === "RSV-999")); // -1
true for the second item, it's never called for the third. The earlier the match, the fewer calls are made.find returns only the first matching item, so it can't collect every match when there are several. Use filter, which returns an array, when you need all of them, and find when one is enough.
You Get undefined When Nothing Is Found
If you read the value find returned directly, as in target.room, it throws TypeError: Cannot read properties of undefined (reading 'room') when no element matched. Check it with if (target) before reading it.
Checking the Whole Array for true or false — some and every
Say you want to decide from stock levels whether an order's items can all ship together. You could collect the out-of-stock items with filter and count them, but what you want isn't an array — it's a single answer, true or false.
some (a method that returns true if at least one element matches the condition) and every (a method that returns true if every element matches the condition) return a boolean, not an element. You can use them directly as an if condition.
const orderItems = [
{ name: "Refill Bottle", stock: 12 },
{ name: "Hand Soap", stock: 0 },
{ name: "Foaming Net", stock: 5 },
];
// true if at least one matches
console.log(orderItems.some((item) => item.stock === 0)); // true
// true only if all of them match
console.log(orderItems.every((item) => item.stock > 0)); // false
// On an empty array, some is false and every is true
console.log([].some((item) => item.stock > 0)); // false
console.log([].every((item) => item.stock > 0)); // true
some and every have their answer by the second item, which has 0 in stock. every finishes as soon as it finds an element that doesn't match.On an empty array, no element matches the condition, so some is false, and no element fails the condition either, so every is true. The table below sums up what the four search methods in this article return.
| Method | Returns | When no element matches |
|---|---|---|
| find | The first matching element itself | undefined |
| findIndex | The position of the first matching element | -1 |
| some | true if at least one matches | false |
| every | true if all match | false (true for an empty array) |
Knowledge Check
Answer each question one by one.
Q2If you call sort() on [1180, 980, 25] without a compare function, what order do you get?
Q3Which method do you use to check, as a single boolean, whether every stock count is 1 or more?