jsguides

Array.prototype.findIndex()

findIndex(callbackFn, thisArg)

findIndex() iterates through an array and returns the index of the first element that passes the test function. If no element passes, it returns -1. Unlike find() which returns the element itself, findIndex() gives you the position in the array.

Syntax

arr.findIndex(callbackFn, thisArg)

Parameters

ParameterTypeDefaultDescription
callbackFnfunction-Function to test each element
elementany-Current element being processed
indexnumber-Index of current element
arrayarray-Array that findIndex was called on
thisArgobjectundefinedValue to use as this when executing callbackFn

The callback function should return true for elements you want to find.

Examples

Finding a user position

const users = [
  { name: "Alice", role: "admin" },
  { name: "Bob", role: "editor" },
  { name: "Charlie", role: "admin" }
];

const firstAdmin = users.findIndex(user => user.role === "admin");
console.log(firstAdmin);
// 0 (Alice is at index 0)

// If not found, returns -1
const notFound = users.findIndex(user => user.role === "owner");
console.log(notFound);
// -1

The user-object example above works with complex data, but findIndex() is just as useful with simple arrays. The next example searches a plain number array for the first even value. The callback n => n % 2 === 0 runs from left to right, testing each element until the remainder is zero. Because findIndex() stops at the first match, it hits 8 at index 3 and never looks at 9 or 10.

Finding the first even number

const numbers = [1, 3, 5, 8, 9, 10];

const firstEven = numbers.findIndex(n => n % 2 === 0);
console.log(firstEven);
// 3 (the number 8 at index 3)

So far each callback has been a self-contained arrow function. When your test depends on data from outside the array, you can pass a context object as the second argument to findIndex(). The callback then receives that object as this, which lets you write a reusable test function that compares array elements against a dynamic threshold without hardcoding the cutoff value.

Using thisArg

const threshold = { min: 10 };

const index = [5, 8, 12, 3].findIndex(function(item) {
  return item > this.min;
}, threshold);

console.log(index);
// 2 (12 is the first value greater than 10)

Once you know how findIndex() locates a match, a natural next step is to act on the result. A common real-world pattern pairs findIndex() with bracket assignment to update an element in place. After finding the index, you check that it is not -1 (meaning the item was found), then overwrite the original with a modified copy.

Common Patterns

Finding and updating an element

const items = [
  { id: 1, name: "Widget", price: 29.99 },
  { id: 2, name: "Gadget", price: 49.99 }
];

// Find the index, then update
const idx = items.findIndex(item => item.id === 2);
if (idx !== -1) {
  items[idx] = { ...items[idx], price: 39.99 };
}
console.log(items[1].price);
// 39.99

When you only need a property of the matched element — such as its price — you can chain the index result directly into a bracket access. The optional chaining operator ?. protects against the -1 case by short-circuiting to undefined instead of throwing on products[-1]. The next example shows this pattern with a product inventory search.

Chaining with other array methods

const products = [
  { name: "Laptop", inStock: false, price: 999 },
  { name: "Mouse", inStock: true, price: 29 },
  { name: "Keyboard", inStock: true, price: 79 }
];

// Find first in-stock item, then get its price
const firstInStockIdx = products.findIndex(p => p.inStock);
const price = products[firstInStockIdx]?.price;

console.log(price);
// 29

When to Use

  • When you need the position of an element instead of the element itself
  • When you will use the index for further operations (splicing, updating)
  • When searching large arrays where finding the position is more efficient than searching twice

When not to use

  • Use find() if you need the element, not the index
  • Use indexOf() for simple value equality checks (faster)
  • Use includes() if you only need to know whether an element exists

findIndex() vs indexOf()

indexOf() tests for strict equality (===), which works for primitives but cannot match objects. findIndex() accepts a callback, so it works with any condition:

const nums = [10, 20, 30, 40];

nums.indexOf(20);                     // 1 — works for primitives
nums.findIndex(n => n === 20);        // 1 — equivalent

const users = [{ id: 1 }, { id: 2 }];
users.indexOf({ id: 1 });             // -1 — different object reference
users.findIndex(u => u.id === 1);     // 0 — works correctly

Use indexOf() when searching an array of primitives — it is faster because it skips the callback overhead. Use findIndex() for anything more complex.

findIndex() on sparse arrays

findIndex() visits every index from 0 to length - 1, including empty slots in sparse arrays. The callback receives undefined for empty slots, which matters if your test function does not guard against undefined:

const sparse = [1, , 3];   // index 1 is empty
sparse.findIndex(x => x === undefined);  // 1 — empty slot matches

This differs from filter(), which skips empty slots entirely. If you create sparse arrays intentionally, be aware that findIndex() will find the holes.

findIndex() does not mutate

Like most array search methods, findIndex() does not modify the original array. It reads each element in order and returns as soon as the callback returns true. If the callback never returns true, it returns -1 after visiting every element.

When findIndex() is the better choice

findIndex() is the right method when you need the position of a match, not the match itself. That is useful for update flows, delete operations, and any logic that needs to feed the index into another array method such as splice() or bracket assignment. It lets you find and act on the same element without searching a second time.

It also makes intent clear in code that is about placement. If the next step depends on where the item lives in the array, findIndex() keeps that detail front and center. The callback gives you enough flexibility to test complex conditions while still returning a simple numeric answer.

See Also