Searching arrays
Find items and positions by exact value or by a callback condition with indexOf, includes, find, findLast, some, and every.
- 01Pick exact-value methodsUse
indexOf,lastIndexOf, andincludeswithout falling forNaN, object-reference, or index 0 bugs. - 02Search with callbacksUse
find,findIndex,findLast, andfindLastIndexwhen the match is a condition. - 03Ask yes/no questionsChoose
someandevery, and explain their short-circuit and empty-array behavior.
What searching an array means
Searching an array means asking, “where is the item I care about?” or “does any item have the shape I need?” Arrays are ordered, so JavaScript can scan them from left to right, from right to left, or until a callback says the search is finished.
The curriculum summary for this lesson is short and important: arrays let you find items and positions by value or by condition. A value search is like looking for the exact string "draft". A condition search is like looking for the first product whose price is above 20, even if you do not know that product object ahead of time.
Imagine a shelf of books. If you know the exact title, you can scan from the left and say, “it is in slot 3,” or “not on the shelf.” If you only care whether it exists, you can answer yes or no. If the question is “show me the first book written after 2010,” you need a test for each book. That is the difference between exact-value methods and callback methods.
- In real life: Read titles from left to right
- In JavaScript:
indexOfscans indexes from 0 upward - In real life: Report the shelf position
- In JavaScript: An index such as
2, or-1for not found - In real life: Ask only “is it there?”
- In JavaScript:
includesreturnstrueorfalse - In real life: Ask for the first book after 2010
- In JavaScript:
finduses a callback test
Where the analogy stops: A bookshelf title search is visual. JavaScript does exact comparisons and callback calls, so two identical-looking object literals are still different objects unless they are the same reference.
This lesson assumes you have met arrays, strict comparisons in the Comparisons lesson, object references in Objects & references, and callbacks in Function expressions & callbacks and Arrow functions. We will keep naming those ideas as they appear.
indexOf, lastIndexOf & includes
EXACT VALUEUse indexOf, lastIndexOf, and includes when you already have the exact value you are looking for. They do not call your function. They compare the search value to array items.
array.indexOf(value)returns the first index, or-1.array.lastIndexOf(value)returns the last index, or-1.array.includes(value)returnstrueorfalse.
const codes = ["A1", "B2", "A1", NaN];const firstA1 = codes.indexOf("A1");const lastA1 = codes.lastIndexOf("A1");const hasNaN = codes.includes(NaN);const nanIndex = codes.indexOf(NaN); const saved = { id: 1 };const objects = [saved, { id: 1 }];const sameReference = objects.indexOf(saved);const newObject = objects.indexOf({ id: 1 }); console.log(firstA1, lastA1, hasNaN, nanIndex);console.log(sameReference, newObject);Read the first half first. The string "A1" appears at indexes 0 and 2, so indexOf("A1") is 0 and lastIndexOf("A1") is 2. Both methods use -1 as the “not found” value because 0 is a real index.
Now the famous edge case: NaN never equals itself with strict equality. That is why indexOf(NaN) is -1. includes uses a slightly different sameness rule called SameValueZero, so includes(NaN) is true.
Objects bring the reference rule from Objects & references. Two object literals can look identical and still be different objects. Searching for the saved reference works; searching for a brand-new { id: 1 } does not find the earlier object.
These methods also accept a starting position: names.indexOf("Ada", 2), names.lastIndexOf("Ada", 3), and names.includes("Ada", 2). Positive numbers skip earlier items. Negative numbers count from the end before the scan begins. The array is not changed.
Strings have methods with similar names, such as "banana".indexOf("na") and "banana".includes("na"). This lesson is about array methods; the String methods lesson revisits text-specific searching.
find & findIndex
STEP THROUGHExact-value search is not enough for real data. You often have an array of objects and a condition: first out-of-stock product, first expensive invoice, first user with a missing profile. That is when you use a callback.
find(callback)returns the first item whose callback returns a truthy value, orundefined.findIndex(callback)returns the first matching index, or-1.
With find, you stop describing the exact item and start describing the test. JavaScript asks your callback about each item in order. The first time the callback says yes, the search is done.
- In real life: “Show me the first book after 2010”
- In JavaScript:
books.find(book => book.year > 2010) - In real life: The librarian checks one book at a time
- In JavaScript: The callback receives one item at a time
- In real life: The first matching book is handed to you
- In JavaScript:
findreturns the item itself
Where the analogy stops: A librarian understands meaning. JavaScript only knows the exact boolean result your callback returns for each item.
Step through the recording. Change the condition and notice two details: the callback gets one product at a time, and find stops as soon as the answer is known.
Predict which product is the first match. Then step through the real callback calls and watch where find stops.
script
{ name: "Tea", price: 8, inStock: true }, { name: "Tent", price: 55, inStock: false }, { name: "Torch", price: 24, inStock: true },]; const match = products.find((product) => { return product.price > 20;}); console.log(match?.name);const products = [ { name: "Tea", price: 8, inStock: true }, { name: "Tent", price: 55, inStock: false }, { name: "Torch", price: 24, inStock: true }, { name: "Notebook", price: 12, inStock: false },];products.find((product) => product.price > 20);find returned Tent after 2 callback calls. Visited: Tea → Tent.
filter visits every item and returns all matches, while find stops at the first match.The lab includes filter only for contrast. The next array lesson, map, filter & reduce, covers it properly. Remember the difference now: find returns the first match; filter returns all matches in a new array.
findLast & findLastIndex
ES2023Sometimes the best answer is nearest the end: the latest error in a log, the most recent unpaid invoice, the last message from a person. findLast and findLastIndex run the same kind of callback as find, but they search from right to left.
const logs = ["info", "error", "warn", "error"];const lastError = logs.findLast((entry) => entry === "error");const lastErrorIndex = logs.findLastIndex((entry) => entry === "error"); console.log(lastError);console.log(lastErrorIndex);console.log(typeof Array.prototype.findLast === "function");The last error is error at index 3. This browser says findLast support is true.
findLast and findLastIndex are ES2023 methods. Modern browsers support them broadly; feature checking is still easy.These methods are newer ES2023 additions and are broadly available in modern browsers. When you support older environments, feature-check with typeof Array.prototype.findLast === "function" or use a build step/polyfill strategy chosen by your project.
some & every
BOOLEANNot every search needs the item or the index. Sometimes the question is yes/no: “is there at least one?” or “do all of them?”
some(callback)returnstrueas soon as one callback result is truthy.every(callback)returnsfalseas soon as one callback result is falsy.
A teacher can stop reading once one student is absent. To say everyone is present, every name must pass the check. One question needs one yes; the other needs no no answers.
- In real life: Is anyone absent?
- In JavaScript:
students.some(student => student.absent) - In real life: Is everyone present?
- In JavaScript:
students.every(student => student.present) - In real life: Stop as soon as the answer is known
- In JavaScript: Both methods short-circuit
Where the analogy stops: A register can be incomplete. JavaScript only answers for items in the array.
const scores = [84, 91, 77];const hasPerfect = scores.some((score) => score === 100);const allPassing = scores.every((score) => score >= 60); const none = [];console.log(hasPerfect);console.log(allPassing);console.log(none.some(Boolean));console.log(none.every(Boolean));some asks at least one; every asks all.The scores have no perfect score (false) and all pass (true). On an empty array, some is false and every is true.
some stops at the first true callback result. every stops at the first false callback result.The empty-array results feel strange at first. [].some(Boolean) is false because there is no passing item. [].every(Boolean) is true because there is no failing item. Mathematicians call that vacuous truth; in plain English, “all items pass” is not contradicted by any item when the list is empty.
Which method answers this question?
INTERACTIVE| Method | Returns | Best for | Stops early? |
|---|---|---|---|
indexOf(value) | First index, or -1 | Exact value search with strict equality-style matching | No callback |
lastIndexOf(value) | Last index, or -1 | Exact value search from the right | No callback |
includes(value) | Boolean | Exact membership, including NaN | No callback |
find(callback) | Item, or undefined | First item that passes a test | Stops at first true |
findIndex(callback) | Index, or -1 | Position of first item that passes | Stops at first true |
findLast(callback) | Item, or undefined | Last item that passes | Searches right to left |
findLastIndex(callback) | Index, or -1 | Position of last item that passes | Searches right to left |
some(callback) | Boolean | At least one item passes | Stops at first true |
every(callback) | Boolean | All items pass | Stops at first false |
Sort the questions by the kind of answer they need. This is the most practical skill: before typing a method name, decide whether you need an exact value, a first match, a last match, or only a boolean.
- Does
rolescontain the exact stringadmin? - Give me the first product with
price > 100. - Where is the first product with
price > 100? - Show the most recent log entry whose
leveliserror. - What position holds the most recent
errorlog? - Is any product out of stock?
- Did every score pass?
- Where is the exact string
draftinstatuses?
Sort by the kind of answer you need. Each card's explanation names a good method.
Where you’ll use this
Array search methods show up anywhere a page holds a list of real data. A shopping cart checks whether an item is already present before adding another row. A settings page finds the first invalid field. A dashboard looks for the latest error. A permissions check asks whether a user has one required role or every required role.
Start the variable name with the question you are asking: has or is often pairs with some, all with every, first with find, and last with findLast.
These methods do not mutate the array. They inspect it and return an answer. Later lessons cover methods that produce transformed arrays, sort arrays, or copy arrays safely.
Common misconceptions
“indexOf is a safe boolean check.”
No. indexOf returns a number. Index 0 is falsy and -1 is truthy, so if (arr.indexOf(x)) is wrong in both directions. Use includes or compare with !== -1.
“find returns all matches.”
find returns the first matching item. Use filter when you need all matches; that is the headline contrast with the next lesson.
“Object literals with the same properties are equal.”
Arrays compare objects by reference for exact searches. Search for the saved object reference, or use find(obj => obj.id === 1) when you mean “same id.”
“every on an empty array should be false.”
It is true. every looks for a counterexample. An empty array has no item that fails the test.
“findLast is a different kind of callback.”
It uses the same callback idea as find; only the direction changes. It starts at the final index and moves left.
Practice: search arrays on purpose
5 EXERCISESPredict both lines before you run the code.
const values = [NaN, "NaN", NaN];
console.log(values.indexOf(NaN));
console.log(values.includes(NaN));The first log is -1; the second is true. This is the one everyday reason to prefer includes for membership checks.
Which product name prints?
const products = [
{ name: "Tea", inStock: true },
{ name: "Tent", inStock: false },
{ name: "Torch", inStock: true },
];
const match = products.find((product) => !product.inStock);
console.log(match.name);const products = [
{ name: "Tea", inStock: true },
{ name: "Tent", inStock: false },
{ name: "Torch", inStock: true },
];
const match = products.find((product) => !product.inStock);
console.log(match.name);find tests Tea first, then Tent. Tent is the first product where !product.inStock is true, so the printed name is Tent.
What boolean does the program print?
const scores = [72, 88, 61];
console.log(scores.every((score) => score >= 60));const scores = [72, 88, 61];
console.log(scores.every((score) => score >= 60));All three scores are at least 60, so every returns true.
Which message prints?
const logs = [
{ level: "info", message: "ready" },
{ level: "error", message: "timeout" },
{ level: "error", message: "retry failed" },
];
const latest = logs.findLast((entry) => entry.level === "error");
console.log(latest.message);const logs = [
{ level: "info", message: "ready" },
{ level: "error", message: "timeout" },
{ level: "error", message: "retry failed" },
];
const latest = logs.findLast((entry) => entry.level === "error");
console.log(latest.message);Searching from the right finds the final error object first, so the message is retry failed.
The code should print allowed, because admin is present at index 0. Replace the condition with a safe one.
const roles = ["admin", "editor"];
if (roles.indexOf("admin")) {
console.log("allowed");
}const roles = ["admin", "editor"];
if (roles.indexOf("admin") !== -1) {
console.log("allowed");
}The fixed condition treats the return value correctly. includes is even clearer when you only need a boolean.
Quiz: check your understanding
7 QUESTIONSTry each answer before reading the explanations. Many wrong answers name a real bug.
Question 1 of 7What do indexOf and lastIndexOf print?
Read the code, then predictconst letters = ["a", "b", "a"]; console.log(letters.indexOf("a")); console.log(letters.lastIndexOf("a"));Choose an answer to see the explanation.
Question 2 of 7Which method should check whether an array contains
NaN?Choose an answer to see the explanation.
Question 3 of 7What does
findprint for prices?Read the code, then predictconst products = [{ price: 10 }, { price: 30 }, { price: 50 }]; console.log(products.find((product) => product.price > 20).price);Choose an answer to see the explanation.
Question 4 of 7Which method returns
-1when no callback match is found?Choose an answer to see the explanation.
Question 5 of 7What do
findLastandfindLastIndexprint?Read the code, then predictconst logs = ["error", "warn", "error"]; console.log(logs.findLast((level) => level === "error")); console.log(logs.findLastIndex((level) => level === "error"));Choose an answer to see the explanation.
Question 6 of 7What do
someandeveryprint for an empty array?Read the code, then predictconsole.log([].some(Boolean)); console.log([].every(Boolean));Choose an answer to see the explanation.
Question 7 of 7What is the bug in
if (items.indexOf("apple"))?Choose an answer to see the explanation.
Key takeaways
indexOfandlastIndexOfreturn indexes or-1;includesreturns a boolean.includes(NaN)works, whileindexOf(NaN)does not.- Objects are compared by reference, so use callback methods when you mean “same property value.”
findandfindIndexstop at the first callback match;findLastandfindLastIndexsearch from the right.someasks “at least one?” andeveryasks “all?”; both short-circuit.- Never write
if (arr.indexOf(x)). Useincludesor compare the index to-1.
Remember the one-liner.
Array search methods either compare an exact value, run a callback until a match is known, or return a yes/no answer.
Up next: map, filter & reduce.