Array.filter()
Array.prototype.filter() builds a new array containing only the elements for which a callback returns a truthy value. It is the go-to tool whenever you need a subset of an array — active users, valid records, non-empty strings — without mutating the original data. Because it returns a fresh array, filter fits naturally into functional, immutable-style pipelines alongside map and reduce.
The predicate function
filter takes a predicate: a function that receives each element and returns true (keep it) or false (drop it). The callback is invoked with three arguments — element, index, and the original array — though most predicates only use the first.
const numbers = [1, 2, 3, 4, 5, 6];
const evens = numbers.filter((n) => n % 2 === 0);
console.log(evens);
console.log(numbers); // original is untouched
Output:
[ 2, 4, 6 ]
[ 1, 2, 3, 4, 5, 6 ]
The result is always a new array. Its length is between 0 (nothing matched) and the original length (everything matched). Element order is preserved.
const users = [
{ name: "Ada", active: true },
{ name: "Linus", active: false },
{ name: "Grace", active: true },
];
const activeUsers = users.filter((user) => user.active);
console.log(activeUsers.map((u) => u.name));
Output:
[ 'Ada', 'Grace' ]
Using the index and array arguments
The extra arguments are handy for position-based logic, such as keeping every other element or de-duplicating while preserving order.
const letters = ["a", "b", "a", "c", "b", "d"];
// Keep the first occurrence of each value
const unique = letters.filter(
(value, index, arr) => arr.indexOf(value) === index
);
console.log(unique);
Output:
[ 'a', 'b', 'c', 'd' ]
Removing falsy values
A common cleanup task is stripping out falsy entries — false, 0, "", null, undefined, and NaN. Passing the Boolean constructor as the predicate does exactly this, because each value is coerced to a boolean.
const messy = [0, "hello", "", null, 42, undefined, NaN, "world"];
const clean = messy.filter(Boolean);
console.log(clean);
Output:
[ 'hello', 42, 'world' ]
Tip:
arr.filter(Boolean)is the idiomatic way to drop falsy values. It is concise, allocation-light, and instantly recognizable to other JavaScript developers.
Combining filter with map
filter and map compose beautifully. Filter first to narrow the set, then map to transform what remains. Reading the chain top to bottom describes the data flow.
const products = [
{ name: "Keyboard", price: 45, inStock: true },
{ name: "Monitor", price: 220, inStock: false },
{ name: "Mouse", price: 25, inStock: true },
{ name: "Webcam", price: 80, inStock: true },
];
const affordableInStock = products
.filter((p) => p.inStock && p.price < 100)
.map((p) => `${p.name}: $${p.price}`);
console.log(affordableInStock);
Output:
[ 'Keyboard: $45', 'Mouse: $25', 'Webcam: $80' ]
Order matters for performance: filtering before mapping means you transform fewer elements. If you find yourself filtering and transforming in a single pass over a huge dataset, consider reduce or flatMap to avoid the intermediate array.
Immutability
filter never modifies the source array — it reads each element and returns a brand-new array. This makes it safe in state management code (React, Redux, signals) where mutating existing arrays causes subtle bugs. To “remove” an item immutably, filter it out:
const todos = [
{ id: 1, text: "Write docs" },
{ id: 2, text: "Review PR" },
{ id: 3, text: "Deploy" },
];
const remaining = todos.filter((todo) => todo.id !== 2);
console.log(remaining.length); // 3 -> 2
console.log(todos.length); // still 3
Output:
2
3
Note that filter performs a shallow copy. The new array holds references to the same objects, so mutating a nested object affects both arrays. Clone objects (via spread or structuredClone) if you need full isolation.
filter vs. similar methods
| Method | Returns | Use when |
|---|---|---|
filter | New array of all matches | You want every matching element |
find | First matching element (or undefined) | You want only one match |
some | Boolean | You only need to know if any element matches |
every | Boolean | You need to confirm all elements match |
map | New array, same length | You transform rather than select |
Gotcha:
filteralways scans the entire array. If you only need the first match, usefind, which short-circuits and stops early.
Best Practices
- Keep predicates pure — no side effects, no mutation — so the pipeline stays predictable.
- Use
arr.filter(Boolean)to remove falsy values instead of writing an explicit truthiness check. - Filter before map to transform fewer elements, and chain methods for readable data flow.
- Reach for
find,some, oreverywhen you do not actually need the full subset; they short-circuit. - Remember
filteris shallow — deep-clone elements when you need full immutability. - For very large arrays where you filter and transform together, prefer
reduceorflatMapto skip the intermediate array.