Use array.find() with a test on the property: users.find((user) => user.id === 2). It returns the first matching object, or undefined if none matches. For every match use filter(); to pull one property out of each object use map((user) => user.name).
Arrays of objects are everywhere in JavaScript: rows from an API, items in a cart, users in a list. The questions are always the same: get the object whose id is 2, get all the objects in a category, or get just the names. Each one has its own array method, and they differ in what they return when nothing matches. This page covers find(), findIndex(), findLast(), filter() and map(), then the point where scanning the array on every lookup is the wrong design and a Map should take over. Each example runs on this page: hit Run, then edit the code and run it again.
1array.find()Recommended
find(test) calls your test function on each element in order and returns the first element it returns true for. It stops there, so it never scans more of the array than it has to. The test is ordinary code, so you can match on any property, or on several at once with &&. When nothing passes, the result is undefined: pair it with optional chaining (?.) and ?? to read a property without a crash.
Output
Prints { id: 2, name: 'Linus', role: 'user' }, Ada, undefined, then unknown. Writing users.find((user) => user.id === 99).name without the ?. would throw a TypeError, because you can't read a property of undefined. find() arrived in ES2015; older answers use filter(test)[0] or a for loop with break, which get the same object but are either slower or longer. If you only need a yes/no answer, not the object, some() with the same test is the direct way; see check if an array contains a value.
2findIndex(), findLast() and findLastIndex()
When you need to replace or remove the object, you need its position, not just the object. findIndex(test) takes the same kind of test and returns the index of the first match, or -1 when there is none (the same sentinel indexOf() uses). Since ES2023 both methods have mirror images that search from the end of the array: findLast() and findLastIndex(), handy for "the most recent entry that…" without reversing a copy first.
Output
Prints 2, -1, { id: 'b', title: 'Fix bug', done: true }, Ship release, then 3. Always check for -1 before using the index: tasks[-1] is just undefined, and tasks.splice(-1, 1) would quietly remove the last task instead of doing nothing.
3Every match: filter()
find() stops at the first hit. When several objects can match and you want all of them, use filter(test): it runs the test on every element and returns a new array of the ones that passed, in their original order. The original array is left untouched.
Output
Prints 3, then the Keyboard and Mouse objects, then []. Because filter() always returns an array, check for "nothing found" with result.length === 0, not if (!result): an empty array is truthy. The objects inside the new array are the same objects as in products, not copies.
4Extract one property as an array: map()
The other common request is the reverse: not the object, but one property from every object, such as all the names. map(fn) calls fn on each element and collects the return values into a new array of the same length. Return the property and you have "pluck", which libraries like Lodash used to provide as a separate function. Chain it after filter() to pluck from just the matching objects.
Output
Prints [ 'Ada', 'Linus', 'Grace' ], [ 1, 2, 3 ], [ 'Ada', 'Grace' ], then three { id, name } objects. The extra parentheses in ({ id, name }) matter: without them the braces would be read as a function body, not an object literal. If a property can repeat and you want each value once, wrap the result in a Set: [...new Set(users.map((u) => u.role))].
5Which should you use?
| Method | Returns | If nothing matches | Best for |
|---|---|---|---|
| arr.find(fn) | First matching object | undefined | Get one object by id or key, the default |
| arr.findIndex(fn) | Index of first match | -1 | Replacing or removing the object |
| arr.findLast(fn) / findLastIndex(fn) | Last match / its index | undefined / -1 | The most recent entry that matches |
| arr.filter(fn) | Array of all matches | [] (empty array) | Several objects can match |
| arr.map(fn) | Array of values | n/a (one value per element) | Extracting one property from each object |
| new Map(...) / Object.groupBy() | Index built once | undefined | Many lookups against the same array |
6Searching repeatedly? Build a Map or group once
Every method above walks the array, so each lookup costs time proportional to its length. That is fine for one search, but calling find() inside a loop over another list turns into a lot of scanning. The fix is to index the array once: a Map keyed by the property gives direct lookups after that. When several objects share a key, ES2024's Object.groupBy() builds the groups in one call.
Output
Prints Grace, undefined, 3, [ 'Ada', 'Grace' ], then the grouped object. Node shows it as [Object: null prototype] { admin: [ … ], user: [ … ] } because Object.groupBy() returns an object with no prototype, so a key such as "toString" can't collide with an inherited method. If a key maps to more than one object, new Map(...) keeps only the last one; use Object.groupBy(), or Map.groupBy() for non-string keys, when duplicates are possible. Plain objects work as an index too (Object.fromEntries(users.map((u) => [u.id, u]))), but their keys are always strings.
Frequently asked questions
What is the difference between find() and filter()?
find() returns the first matching element itself and stops searching, or undefined if nothing matches. filter() checks every element and always returns an array: all the matches, or [] if there are none. Use find() when you expect one result (an id lookup) and filter() when there can be several. filter(fn)[0] gets the same object as find(fn) but scans the whole array to do it.
Does find() return a copy of the object?
No. It returns a reference to the object that is in the array, so changing it changes the array too: after const u = users.find((x) => x.id === 2); u.name = "Torvalds", users[1].name is Torvalds. If you need an independent copy, spread it ({ ...u }) for a shallow copy or use structuredClone(u) for a deep one.
How do I find an object that matches two properties?
Put both conditions in the test function with &&: users.find((u) => u.role === "admin" && u.name.startsWith("G")). The test is ordinary JavaScript, so any expression that returns true or false works, including ||, ranges like u.price < 50, or nested properties with ?..
Why does my if (found) check fail when find() matched something?
Because the match itself was falsy. find() returns the element, so on an array like [0, 1, 2], find((n) => n < 1) returns 0, and if (0) is false. For arrays of objects this never bites, because every object is truthy, but for primitives compare with found !== undefined, or use findIndex() and check for -1.
How do I get the unique values of a property from an array of objects?
Pluck the property with map(), then pass the result through a Set and spread it back into an array: [...new Set(users.map((u) => u.role))] returns each role once, in first-seen order.
How do I find an object by property without find()?
A for...of loop that breaks on the first match does the same job and works in any engine: let found; for (const u of users) { if (u.id === 3) { found = u; break; } }. That is how it was written before ES2015. Today find() says the same thing in one line, so reach for the loop only when you also need to do other work on each element as you go.
Run it yourself
Open any of these in the full JavaScript editor: tweak, run, and share.
JavaScript playground