Finding and ordering data are two of the most common operations in any application โ displaying a leaderboard, filtering a product list, locating a user record, or sorting search results. JavaScript provides a comprehensive set of methods for both. In this lesson you will master the full array search toolkit (find, findIndex, indexOf, includes, some, every) and the sort method with custom comparators for sorting numbers, strings, dates, and multi-column data.
Search Methods Comparison
| Method | Returns | Best For |
|---|---|---|
includes(val) |
Boolean | “Does this value exist?” โ primitives only |
indexOf(val) |
First index or -1 |
Find position of primitive value |
lastIndexOf(val) |
Last index or -1 |
Find last occurrence of primitive value |
find(fn) |
First matching element or undefined |
Find one object by condition |
findIndex(fn) |
Index of first match or -1 |
Find position of object by condition |
findLast(fn) |
Last matching element | Find last match (ES2023) |
some(fn) |
Boolean โ true if any match | “Does any element satisfy this?” |
every(fn) |
Boolean โ true if all match | “Do all elements satisfy this?” |
filter(fn) |
New array of all matches | Get all matching elements |
sort() Comparator Rules
| Return Value | Meaning | Result |
|---|---|---|
| Negative number | a comes before b | a, b |
| 0 | a and b are equal | Order unchanged |
| Positive number | b comes before a | b, a |
a - b |
Numbers ascending | Lowest first |
b - a |
Numbers descending | Highest first |
a.localeCompare(b) |
Strings ascending | Alphabetical AโZ |
Default Sort Behaviour โ The Common Gotcha
| Input | Default sort() | Correct sort |
|---|---|---|
[10, 9, 2, 21, 100] |
[10, 100, 2, 21, 9] โ lexicographic! |
[2, 9, 10, 21, 100] with (a,b) => a-b |
['banana','apple','cherry'] |
['apple','banana','cherry'] โ works |
Use localeCompare for accented chars |
| Array of objects | Converts to string โ breaks completely | Always provide comparator for objects |
find and findIndex stop at the first match โ they short-circuit like &&. If you need all matching elements, use filter. If you only need to know whether a match exists, use some โ it is more semantically clear than checking find(fn) !== undefined.||: (a, b) => a.dept.localeCompare(b.dept) || a.name.localeCompare(b.name). The first comparison only returns 0 (falsy) when the departments are equal, at which point the second comparison determines the order.
sort() with no comparator converts elements to strings and sorts them lexicographically. This means [10, 9, 2, 21].sort() gives [10, 2, 21, 9] โ because '10' < '2' alphabetically. Always provide a numeric comparator: .sort((a, b) => a - b). This is one of the most common JavaScript gotchas.Basic Example
const users = [
{ id: 1, name: 'Alice', age: 30, dept: 'Engineering', salary: 95000, active: true },
{ id: 2, name: 'Bob', age: 25, dept: 'Marketing', salary: 72000, active: true },
{ id: 3, name: 'Carol', age: 35, dept: 'Engineering', salary: 88000, active: false },
{ id: 4, name: 'Dave', age: 28, dept: 'Engineering', salary: 102000, active: true },
{ id: 5, name: 'Eve', age: 25, dept: 'Marketing', salary: 68000, active: true },
];
// โโ find โ first object match โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
const alice = users.find(u => u.name === 'Alice');
console.log(alice?.name); // 'Alice'
const senior = users.find(u => u.age >= 35);
console.log(senior?.name); // 'Carol'
// โโ findIndex โ position of object โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
const idx = users.findIndex(u => u.id === 3);
console.log(idx); // 2
// โโ some / every โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
const anyInactive = users.some(u => !u.active); // true (Carol)
const allEngineers = users.every(u => u.dept === 'Engineering'); // false
const allEarning = users.every(u => u.salary > 50000); // true
console.log(anyInactive, allEngineers, allEarning);
// โโ includes vs find โ primitives vs objects โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
const ids = [1, 2, 3, 4, 5];
console.log(ids.includes(3)); // true โ works for primitives
console.log(users.includes({ id: 1 })); // false โ objects compared by reference
// โโ SORT โ always copy first โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// Numbers ascending
const byAgeAsc = [...users].sort((a, b) => a.age - b.age);
// Numbers descending
const bySalDes = [...users].sort((a, b) => b.salary - a.salary);
// Strings alphabetical
const byName = [...users].sort((a, b) => a.name.localeCompare(b.name));
// Boolean โ active first
const actFirst = [...users].sort((a, b) => Number(b.active) - Number(a.active));
console.log(byAgeAsc.map(u => `${u.name}(${u.age})`));
// ['Bob(25)','Eve(25)','Dave(28)','Alice(30)','Carol(35)']
console.log(bySalDes.map(u => `${u.name}:$${u.salary}`));
// ['Dave:$102000','Alice:$95000','Carol:$88000','Bob:$72000','Eve:$68000']
// โโ Multi-column sort โ dept asc, then salary desc โโโโโโโโโโโโโโโโโโโโโโโโ
const multiSort = [...users].sort((a, b) =>
a.dept.localeCompare(b.dept) || // primary: dept A-Z
b.salary - a.salary // tiebreaker: salary highest first
);
console.log(multiSort.map(u => `${u.dept}/${u.name}/$${u.salary}`));
// Engineering/Dave/$102000
// Engineering/Alice/$95000
// Engineering/Carol/$88000
// Marketing/Bob/$72000
// Marketing/Eve/$68000
// โโ Binary search on sorted array (manual) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function binarySearch(sortedArr, target) {
let low = 0, high = sortedArr.length - 1;
while (low <= high) {
const mid = Math.floor((low + high) / 2);
if (sortedArr[mid] === target) return mid;
if (sortedArr[mid] < target) low = mid + 1;
else high = mid - 1;
}
return -1;
}
const sorted = [2, 5, 8, 12, 16, 23, 38, 56, 72, 91];
console.log(binarySearch(sorted, 23)); // 5
console.log(binarySearch(sorted, 99)); // -1
How It Works
Step 1 โ find Short-Circuits on First Match
find iterates the array and calls the callback for each element. The moment the callback returns truthy, find returns that element and stops iterating. This makes it efficient for large arrays โ no need to check every element once a match is found.
Step 2 โ some and every Also Short-Circuit
some stops and returns true as soon as one element passes. every stops and returns false as soon as one element fails. For large arrays this is significantly faster than checking all elements with filter().length > 0.
Step 3 โ sort() Uses an In-Place Comparison Sort
JavaScript’s sort uses an internal algorithm (TimSort in V8) that calls your comparator function repeatedly with pairs of elements. The comparator’s return value tells sort which element should come first. Always copy the array with [...arr] before sorting if you need the original order preserved.
Step 4 โ Multi-Column Sort with ||
The || in a.dept.localeCompare(b.dept) || b.salary - a.salary exploits short-circuit evaluation. When departments differ, localeCompare returns a non-zero number (truthy) and || returns that value โ the salary comparison is never evaluated. When departments are equal, localeCompare returns 0 (falsy) and || evaluates the right side โ the salary comparison breaks the tie.
Step 5 โ Binary Search for Sorted Arrays
Linear search (find, indexOf) is O(n) โ it checks every element in the worst case. Binary search is O(log n) โ it halves the search space each iteration. For a sorted array of 1,000,000 elements, linear search may check 1,000,000 elements; binary search checks at most 20. The tradeoff is the array must already be sorted.
Real-World Example: Product Search and Sort UI
// product-search.js
const products = [
{ id: 1, name: 'Laptop', price: 999, category: 'tech', rating: 4.5, inStock: true },
{ id: 2, name: 'Headphones', price: 149, category: 'tech', rating: 4.8, inStock: true },
{ id: 3, name: 'Coffee Mug', price: 15, category: 'kitchen', rating: 4.2, inStock: false },
{ id: 4, name: 'Desk Chair', price: 350, category: 'furniture',rating: 4.6, inStock: true },
{ id: 5, name: 'Notebook', price: 8, category: 'office', rating: 4.0, inStock: true },
{ id: 6, name: 'Monitor', price: 450, category: 'tech', rating: 4.7, inStock: true },
];
function searchProducts({ query = '', category = null, maxPrice = Infinity,
inStockOnly = false, sortBy = 'name', sortDir = 'asc' } = {}) {
const q = query.toLowerCase().trim();
const filtered = products.filter(p => {
if (q && !p.name.toLowerCase().includes(q)) return false;
if (category && p.category !== category) return false;
if (p.price > maxPrice) return false;
if (inStockOnly && !p.inStock) return false;
return true;
});
const comparators = {
name: (a, b) => a.name.localeCompare(b.name),
price: (a, b) => a.price - b.price,
rating: (a, b) => a.rating - b.rating,
};
const cmp = comparators[sortBy] ?? comparators.name;
filtered.sort((a, b) => sortDir === 'desc' ? cmp(b, a) : cmp(a, b));
return filtered;
}
console.log(searchProducts({ category: 'tech', sortBy: 'price', sortDir: 'desc' }));
// Monitor $450, Laptop $999, Headphones $149 โ sorted price desc
console.log(searchProducts({ maxPrice: 200, inStockOnly: true, sortBy: 'rating', sortDir: 'desc' }));
// Headphones(4.8), Notebook(4.0) โ in-stock, under $200, best-rated first
Common Mistakes
Mistake 1 โ Sorting numbers without a comparator
โ Wrong โ default sort is lexicographic:
[100, 20, 3].sort() // [100, 20, 3] โ '100' < '20' < '3'
// Result: [100, 20, 3] โ sorted as strings!
โ Correct โ provide a numeric comparator:
[100, 20, 3].sort((a, b) => a - b) // [3, 20, 100]
Mistake 2 โ Using find when all matches are needed
โ Wrong โ find only returns the first match:
const activeUser = users.find(u => u.active); // only the FIRST active user
โ Correct โ use filter to get all matches:
const activeUsers = users.filter(u => u.active); // ALL active users
Mistake 3 โ Forgetting sort mutates the original array
โ Wrong โ sort modifies the original:
const sorted = items.sort((a, b) => a.price - b.price);
// Both items AND sorted now point to the same mutated array
โ Correct โ copy before sorting:
const sorted = [...items].sort((a, b) => a.price - b.price);
// items is unchanged; sorted is a new sorted array
Quick Reference
| Goal | Method | Returns |
|---|---|---|
| Does value exist? | includes(val) |
Boolean |
| Find first object match | find(fn) |
Element or undefined |
| Find position | findIndex(fn) |
Index or -1 |
| Any match? | some(fn) |
Boolean |
| All match? | every(fn) |
Boolean |
| Sort numbers asc | [...arr].sort((a,b) => a-b) |
Sorted copy |
| Sort strings | [...arr].sort((a,b) => a.localeCompare(b)) |
Sorted copy |
| Multi-column sort | .sort((a,b) => cmp1(a,b) || cmp2(a,b)) |
Sorted copy |