skip to content

Searching and Predicate Methods

find, some, every, and includes answer "is it there?" questions and short-circuit as soon as they know the answer. The interview value is in the edge cases: why indexOf can't find NaN, what a predicate returns for a hole, and when a boolean beats a filtered array.

part ofJavaScriptoverview, primer and where to startread it →
on this pageshow

questions

5

In JavaScript, what do Array.prototype.find and Array.prototype.findIndex return when no element satisfies the predicate, and what bug does that difference commonly cause?

level: juniorimportance: must knowfreq 72%

answer

  1. two methods, two different misses
  2. one gives a value, one a position
  3. minus one is truthy
  4. index zero is falsy
  5. compare the result against -1

basics

~20 s

Array.prototype.find returns undefined when nothing matches, while findIndex returns -1. The bug is truthiness: -1 is truthy and index 0 is falsy, so testing findIndex's result directly inverts the check. Compare it against -1 instead.

solid answer

~40 s

Both take a predicate called with `(element, index, array)` and walk the array front to back, stopping at the first element for which the predicate returns a truthy value. `find` returns that **element**, or `undefined` if nothing matches; `findIndex` returns its **index**, or `-1`. The trap is treating either result as a boolean. `-1` is truthy, so `if (arr.findIndex(p))` reports "found" for a miss, and it reports "not found" when the match sits at index 0, which is falsy — the check is wrong in both directions. Write `if (arr.findIndex(p) !== -1)`. `find`'s `undefined` has its own trap: `arr.find(p).name` throws a TypeError on a miss, so guard the result before dereferencing it. Note that `undefined` is also a legitimate element value, so a `find` result of `undefined` does not prove absence — `findIndex` disambiguates.

code

javascript · 9 lines
javascript
const users = [{ id: 7, name: 'ada' }, { id: 9, name: 'linus' }];

console.log(users.find(u => u.id === 42));      // undefined
console.log(users.findIndex(u => u.id === 42)); // -1
console.log(users.findIndex(u => u.id === 7));  // 0

// The trap: -1 is truthy, 0 is falsy
if (users.findIndex(u => u.id === 42)) console.log('wrongly reported as found');
if (users.findIndex(u => u.id === 7) !== -1) console.log('correctly found');

go deeper

for a junior

Know the two return values cold: the element or undefined for find, the index or -1 for findIndex. Say out loud that you compare findIndex's result against -1 rather than testing it as a boolean.

for a middle

Explain why the truthiness test fails in both directions — -1 is truthy, index 0 is falsy — and mention that the predicate stops at the first truthy return, receives (element, index, array), and accepts an optional thisArg.

for a senior

Show how you keep the undefined result from becoming a production TypeError: guard or default before dereferencing, and prefer findIndex when the array can legitimately hold undefined. Be ready to talk about pure predicates and the fixed index range during the scan.

for a principal

Own the API-shape argument: a function that returns a sentinel like -1 pushes an easy-to-forget check onto every caller, so decide where in the codebase lookups return an element, an index, or a hard failure — and make that convention consistent rather than per-call-site.

## What the two methods actually do `find` and `findIndex` are the predicate-driven search methods on `Array.prototype`. Both accept a callback (the *predicate*) and an optional `thisArg`. The predicate is invoked as `callback(element, index, array)` for each index from `0` upward. Iteration stops at the first index where the predicate returns a **truthy** value — not strictly `true`, any truthy value counts, so a predicate ending in `return u.name` "matches" on any non-empty name. The only difference is what comes back: - `find` returns the matching **element**; if the predicate never returns truthy, it returns `undefined`. - `findIndex` returns the matching **index**; on a miss it returns `-1`. ```js const users = [{ id: 7 }, { id: 9 }]; users.find(u => u.id === 9); // { id: 9 } users.findIndex(u => u.id === 9); // 1 users.find(u => u.id === 42); // undefined users.findIndex(u => u.id === 42);// -1 ``` ## Why -1 is the classic bug JavaScript coerces the search result to a boolean whenever you drop it into an `if`. `-1` is a non-zero number, so it is **truthy**. `0` — a perfectly valid "found at the first position" answer — is **falsy**. That makes a bare truthiness test wrong in both directions: ```js if (users.findIndex(u => u.id === 42)) { // runs, even though nothing matched: -1 is truthy } if (users.findIndex(u => u.id === 7)) { // does NOT run, even though it matched at index 0 } ``` The correct form is an explicit comparison: `findIndex(...) !== -1`, or `>= 0`. The identical trap applies to `indexOf`, `lastIndexOf` and `String.prototype.indexOf`, which is why a boolean-returning membership check is preferred when you only need presence. ## Why undefined is the other bug `find` returning `undefined` on a miss means the very common chain `arr.find(p).prop` throws `TypeError: Cannot read properties of undefined` the moment the data does not contain the row you assumed was there. That is one of the most frequent production stack traces in JavaScript codebases: the code works against the developer's seed data and blows up on a user whose record is missing. Capture the result and branch on it, or use optional chaining plus a default, or throw a domain error that names the missing key so the log is diagnosable. There is also an ambiguity: `undefined` can be a genuine array element. `[undefined].find(x => x === undefined)` returns `undefined` — indistinguishable from a miss by the return value alone. When the array can hold `undefined`, use `findIndex` and compare with `-1`, because `-1` is not a valid index and therefore unambiguous. ## Searching from the other end ES2023 added `findLast` and `findLastIndex`, which use the same predicate contract but iterate from the highest index downward and return the **last** match (`-1` / `undefined` on a miss, same as their forward twins). Before ES2023 the common workaround was to reverse the array first, which introduced its own bug: `Array.prototype.reverse` mutates in place, so `arr.reverse().find(p)` silently reorders the caller's array. `findLast` removes the need for that entirely. ## Holes and mutation during the scan Unlike `some`, `every` and `indexOf`, the `find` family does **not** skip holes in a sparse array. It reads every index from `0` to `length - 1`, so a hole is handed to the predicate as `undefined`: ```js Array(3).findIndex(x => x === undefined); // 0 — holes are visited Array(3).some(x => x === undefined); // false — holes are skipped ``` The range of indices is fixed by the `length` captured when the call begins, so elements appended by the predicate itself are never visited; elements changed in place before their turn are seen with their new value. Predicates should be pure — a predicate with side effects turns a search into a construct whose result depends on iteration order. ## Choosing the right member of the family Use `find` when you want the object itself, `findIndex` when you need the position (to replace or remove by position, or to disambiguate a real `undefined` element), and a boolean-returning check when you only care whether anything matched. Reaching for a filtered array and then reading `[0]` or `.length` does the same job but keeps scanning after the answer is known and allocates an array you throw away.

  • How would you find the last matching element rather than the first?
    Use `findLast` or `findLastIndex`, added in ES2023: same predicate contract, but they scan from the highest index down and return the last match, or `undefined` / `-1` on a miss. The older workaround — reversing first — is risky because `reverse` mutates the array in place, so it reorders the caller's data as a side effect of a read.
  • What arguments does the predicate receive, and can you control what `this` is inside it?
    The predicate is called with `(element, index, array)`, so you can use the index or the whole array inside the test. All four methods also accept an optional second argument, `thisArg`, used as the `this` value for a non-arrow predicate. Arrow predicates ignore `thisArg`, which is one reason passing an arrow and closing over what you need is the common style.
  • If `find` returns `undefined`, how do you distinguish "no match" from "matched an element whose value is undefined"?
    You cannot, from the return value alone — both are `undefined`. Use `findIndex` instead: a real match yields an index of `0` or greater, and only a miss yields `-1`, which is never a valid index. This matters for arrays that legitimately hold `undefined` or that are sparse, since the `find` family reads holes as `undefined` rather than skipping them.

Asking "which book is it?" gets you the book or an empty hand; asking "which shelf slot?" gets you a slot number or the number -1 meaning "nowhere" — and "nowhere" is still a number, so it looks like an answer.

saying these in an interview costs you the question

  • Says find returns -1 when nothing matches
  • Writes if (arr.findIndex(p)) as the found check
  • Thinks find returns every matching element
  • Believes find returns the index of the match
  • Assumes findIndex returns undefined on a miss

context

open as a page

What is the difference between Array.prototype.includes and Array.prototype.indexOf, and why can indexOf never find NaN inside an array?

level: middleimportance: must knowfreq 62%

basics

~20 s

Array.prototype.includes returns a boolean and compares with SameValueZero, so it finds NaN; indexOf returns a position or -1 and compares with strict equality, and NaN === NaN is false, so it never matches. They also disagree on holes in sparse arrays.

open as a page

What does Array.prototype.at(-1) return, and how does it differ from writing arr[-1]?

level: juniorimportance: should knowfreq 38%

basics

~20 s

Array.prototype.at(-1) returns the last element, because a negative argument counts back from the end. Bracket notation does not: arr[-1] is an ordinary property lookup for the key "-1", which normally yields undefined, and assigning to it adds a property without changing length.

open as a page

How do Array.prototype.some and Array.prototype.every short-circuit, and what does each of them return for an empty array?

level: middleimportance: should knowfreq 50%

basics

~20 s

Array.prototype.some stops at the first element whose predicate returns truthy and yields true; every stops at the first falsy result and yields false. On an empty array some returns false and every returns true, because every is vacuously satisfied when there is nothing to violate it.

open as a page

A page keeps an array of selected user objects and tests membership with selected.includes(user). After the user list is refetched from the server, the check starts returning false even though the same person is on screen. What is happening, and how do you fix it?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Array.prototype.includes compares objects by reference, not by content. A refetch builds brand-new objects, so the stored one and the fresh one are different values even with identical fields. Search by a stable field instead, with some or find and a predicate.

open as a page