JavaScript Array Filter Method: Complete Tutorial

Master the JavaScript filter() method for selecting array elements by condition. Covers syntax, callback mechanics, real-world filtering patterns, chaining with map and reduce, performance considerations, and common mistakes.

JavaScriptbeginner
13 min read

The filter() method creates a new array containing only the elements that pass a test you define. It is the standard way to select, search, and narrow down data in JavaScript without mutating the original array. If map() transforms every element, filter() decides which elements survive.

What filter() Does

filter() calls a test function on every element. If the function returns true (or any truthy value), the element is included in the result. If it returns false (or falsy), the element is excluded:

javascriptjavascript
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
 
const evens = numbers.filter(n => n % 2 === 0);
console.log(evens); // [2, 4, 6, 8, 10]
 
// Original unchanged
console.log(numbers); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Syntax

javascriptjavascript
const newArray = array.filter(callback(element, index, array), thisArg)
ParameterDescription
elementThe current element being tested
indexIndex of the current element (optional)
arrayThe original array (optional)
thisArgValue to use as this inside the callback (optional)

Return Value

A new array containing only the elements for which the callback returned a truthy value. If no elements pass, returns an empty array []. The original array is never modified.

Basic Examples

Filtering Strings by Length

javascriptjavascript
const words = ["a", "hello", "hi", "extraordinary", "yes", "no"];
 
const longWords = words.filter(word => word.length > 3);
console.log(longWords); // ["hello", "extraordinary"]

Filtering Objects by Property

javascriptjavascript
const products = [
  { name: "Laptop", price: 999, inStock: true },
  { name: "Mouse", price: 29, inStock: false },
  { name: "Keyboard", price: 79, inStock: true },
  { name: "Monitor", price: 349, inStock: true },
  { name: "Webcam", price: 59, inStock: false },
];
 
const available = products.filter(p => p.inStock);
console.log(available.length); // 3
 
const affordable = products.filter(p => p.price < 100);
console.log(affordable.map(p => p.name)); // ["Mouse", "Keyboard", "Webcam"]

Filtering with Multiple Conditions

javascriptjavascript
const employees = [
  { name: "Alice", department: "Engineering", salary: 95000 },
  { name: "Bob", department: "Marketing", salary: 72000 },
  { name: "Carol", department: "Engineering", salary: 110000 },
  { name: "Dave", department: "Engineering", salary: 88000 },
  { name: "Eve", department: "Marketing", salary: 85000 },
];
 
const seniorEngineers = employees.filter(
  emp => emp.department === "Engineering" && emp.salary >= 90000
);
console.log(seniorEngineers.map(e => e.name)); // ["Alice", "Carol"]

The Index Parameter

The second callback argument gives you the element's position, useful for position-based filtering:

javascriptjavascript
const items = ["a", "b", "c", "d", "e", "f"];
 
// Keep only even-indexed elements
const evenIndex = items.filter((_, index) => index % 2 === 0);
console.log(evenIndex); // ["a", "c", "e"]
 
// Keep every third element
const everyThird = items.filter((_, index) => index % 3 === 0);
console.log(everyThird); // ["a", "d"]

Real-World Patterns

Search Implementation

Filtering a dataset by a search term:

javascriptjavascript
function searchProducts(products, query) {
  const lowerQuery = query.toLowerCase().trim();
 
  if (!lowerQuery) return products;
 
  return products.filter(product =>
    product.name.toLowerCase().includes(lowerQuery) ||
    product.category.toLowerCase().includes(lowerQuery) ||
    product.tags.some(tag => tag.toLowerCase().includes(lowerQuery))
  );
}
 
const catalog = [
  { name: "MacBook Pro", category: "Laptops", tags: ["apple", "pro"] },
  { name: "ThinkPad X1", category: "Laptops", tags: ["lenovo", "business"] },
  { name: "Magic Mouse", category: "Accessories", tags: ["apple", "wireless"] },
  { name: "MX Master", category: "Accessories", tags: ["logitech", "ergonomic"] },
];
 
console.log(searchProducts(catalog, "apple").map(p => p.name));
// ["MacBook Pro", "Magic Mouse"]
 
console.log(searchProducts(catalog, "laptop").map(p => p.name));
// ["MacBook Pro", "ThinkPad X1"]

Removing Duplicates

Filter combined with indexOf to keep only the first occurrence:

javascriptjavascript
const withDupes = ["JavaScript", "Python", "JavaScript", "Rust", "Python", "Go"];
 
const unique = withDupes.filter((item, index) => withDupes.indexOf(item) === index);
console.log(unique); // ["JavaScript", "Python", "Rust", "Go"]

For complex objects, use a Set with a key:

javascriptjavascript
const users = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 1, name: "Alice" }, // Duplicate
  { id: 3, name: "Carol" },
];
 
const seen = new Set();
const uniqueUsers = users.filter(user => {
  if (seen.has(user.id)) return false;
  seen.add(user.id);
  return true;
});
console.log(uniqueUsers.length); // 3

Removing Falsy Values

javascriptjavascript
const messy = [0, "hello", "", null, undefined, 42, false, "world", NaN];
 
const clean = messy.filter(Boolean);
console.log(clean); // ["hello", 42, "world"]
The Boolean Trick

Passing Boolean as the callback is shorthand for item => Boolean(item). It removes all falsy values: 0, "", null, undefined, NaN, and false. Be careful: this also removes 0 and "", which might be valid in your data.

Form Validation Errors

javascriptjavascript
function validateForm(fields) {
  const errors = [
    !fields.name && "Name is required",
    !fields.email && "Email is required",
    fields.email && !fields.email.includes("@") && "Email must be valid",
    !fields.password && "Password is required",
    fields.password && fields.password.length < 8 && "Password must be at least 8 characters",
  ].filter(Boolean); // Remove false entries
 
  return errors;
}
 
const result = validateForm({ name: "Alice", email: "bad", password: "123" });
console.log(result);
// ["Email must be valid", "Password must be at least 8 characters"]

Date Range Filtering

javascriptjavascript
const events = [
  { name: "Conference", date: new Date("2026-03-15") },
  { name: "Workshop", date: new Date("2026-04-20") },
  { name: "Meetup", date: new Date("2026-02-10") },
  { name: "Hackathon", date: new Date("2026-05-01") },
  { name: "Webinar", date: new Date("2026-03-28") },
];
 
const march = events.filter(event => {
  const month = event.date.getMonth(); // 0-indexed: March = 2
  return month === 2;
});
console.log(march.map(e => e.name)); // ["Conference", "Webinar"]
 
// Events after today
const upcoming = events.filter(event => event.date > new Date());

Chaining filter() with Other Methods

filter() + map(): Select and Transform

The most common chain in JavaScript:

javascriptjavascript
const orders = [
  { id: 1, status: "completed", total: 150 },
  { id: 2, status: "pending", total: 80 },
  { id: 3, status: "completed", total: 220 },
  { id: 4, status: "cancelled", total: 50 },
  { id: 5, status: "completed", total: 175 },
];
 
const completedTotals = orders
  .filter(order => order.status === "completed")
  .map(order => `Order #${order.id}: $${order.total}`);
 
console.log(completedTotals);
// ["Order #1: $150", "Order #3: $220", "Order #5: $175"]

filter() + reduce(): Select and Aggregate

javascriptjavascript
const expenses = [
  { category: "food", amount: 45 },
  { category: "transport", amount: 30 },
  { category: "food", amount: 62 },
  { category: "entertainment", amount: 100 },
  { category: "food", amount: 28 },
];
 
const totalFood = expenses
  .filter(e => e.category === "food")
  .reduce((sum, e) => sum + e.amount, 0);
 
console.log(totalFood); // 135

filter() + sort(): Select and Order

javascriptjavascript
const students = [
  { name: "Alice", gpa: 3.9 },
  { name: "Bob", gpa: 2.8 },
  { name: "Carol", gpa: 3.5 },
  { name: "Dave", gpa: 3.7 },
  { name: "Eve", gpa: 2.5 },
];
 
const deansList = students
  .filter(s => s.gpa >= 3.5)
  .sort((a, b) => b.gpa - a.gpa)
  .map(s => `${s.name} (${s.gpa})`);
 
console.log(deansList); // ["Alice (3.9)", "Dave (3.7)", "Carol (3.5)"]

filter() vs Other Selection Methods

MethodReturnsBest for
filter(fn)All matching elements (as new array)Getting all matches
find(fn)First matching elementGetting one match
findIndex(fn)Index of first matchFinding position
some(fn)true if any matchExistence check
every(fn)true if all matchValidation
includes(value)true if value existsSimple value check
javascriptjavascript
const scores = [85, 92, 67, 78, 95, 88, 72];
 
// All passing scores
const passing = scores.filter(s => s >= 80);   // [85, 92, 95, 88]
 
// First passing score
const first = scores.find(s => s >= 80);        // 85
 
// Does any score pass?
const hasPass = scores.some(s => s >= 80);       // true
 
// Do all scores pass?
const allPass = scores.every(s => s >= 80);      // false

filter() vs splice() for Removal

When removing elements from an array, you have two approaches:

javascriptjavascript
const items = ["keep", "remove", "keep", "remove", "keep"];
 
// filter: creates new array (immutable)
const filtered = items.filter(item => item !== "remove");
console.log(filtered); // ["keep", "keep", "keep"]
console.log(items);    // ["keep", "remove", "keep", "remove", "keep"] — original safe
 
// splice: modifies in place (mutation)
const items2 = ["keep", "remove", "keep", "remove", "keep"];
for (let i = items2.length - 1; i >= 0; i--) {
  if (items2[i] === "remove") {
    items2.splice(i, 1);
  }
}
console.log(items2); // ["keep", "keep", "keep"] — original changed
Aspectfilter()splice()
Creates new arrayYesNo (modifies original)
Removes by conditionNatural (callback)Requires manual index tracking
Safe for React stateYesNo
Performance (large arrays)O(n) alwaysO(n) per splice, O(n^2) in loops
ReadabilityHigh (declarative)Lower (imperative, backward loop)

Performance Considerations

filter() iterates the entire array and creates a new one. For most applications this is fast:

javascriptjavascript
// Filtering 100K items: ~2-5ms
const large = Array.from({ length: 100_000 }, (_, i) => ({
  id: i,
  active: Math.random() > 0.5,
}));
 
console.time("filter-100k");
const active = large.filter(item => item.active);
console.timeEnd("filter-100k"); // ~2-5ms

For chained operations on very large arrays, combine into a single pass:

javascriptjavascript
// Two passes (filter + map)
const result1 = data.filter(d => d.active).map(d => d.value);
 
// Single pass (reduce)
const result2 = data.reduce((acc, d) => {
  if (d.active) acc.push(d.value);
  return acc;
}, []);
Optimize Only When Needed

Chain filter().map() freely in most code. The two-pass approach is clearer and easier to maintain. Only combine into reduce() if profiling shows the chain is a bottleneck (rare for arrays under 100K elements).

Common Mistakes

Forgetting filter returns a new array (not modifying original):

javascriptjavascript
const items = [1, 2, 3, 4, 5];
 
// Bug: developer expects items to be modified
items.filter(n => n > 3);
console.log(items); // [1, 2, 3, 4, 5] — still the same!
 
// Fix: assign the result
const big = items.filter(n => n > 3);
console.log(big); // [4, 5]

Using filter when you only need one result:

javascriptjavascript
const users = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 3, name: "Carol" },
];
 
// Wasteful: filters the entire array to find one item
const found = users.filter(u => u.id === 2)[0];
 
// Better: stops at first match
const found2 = users.find(u => u.id === 2);

Mutating objects inside filter:

javascriptjavascript
const items = [
  { name: "A", processed: false },
  { name: "B", processed: false },
];
 
// Anti-pattern: using filter for side effects
const unprocessed = items.filter(item => {
  item.processed = true; // Mutation inside filter!
  return !item.processed; // Always false now
});
console.log(unprocessed); // [] — everything was marked processed before the test

Comparing objects by reference:

javascriptjavascript
const target = { id: 1 };
const arr = [{ id: 1 }, { id: 2 }, { id: 3 }];
 
// Bug: objects are compared by reference, not value
const found = arr.filter(item => item === target);
console.log(found); // [] — no match because different object references
 
// Fix: compare by property
const found2 = arr.filter(item => item.id === target.id);
console.log(found2); // [{ id: 1 }]
Rune AI

Rune AI

Key Insights

  • Non-mutating selection: filter() returns a new array with only the elements that pass the test
  • Truthy/falsy evaluation: The callback does not need to return exactly true; any truthy value includes the element
  • filter(Boolean) shortcut: Removes all falsy values (null, undefined, 0, "", false, NaN) in one call
  • Chain with map(): filter().map() is the standard pattern for selecting then transforming data
  • Use find() for single items: When you need one result, find() is more efficient and expressive than filter()[0]
RunePowered by Rune AI

Frequently Asked Questions

Does filter() modify the original array?

No. `filter()` always returns a new array containing only the elements that passed the test. The original array remains completely unchanged. However, if you mutate objects inside the callback (by changing their properties), those changes will reflect in the original because objects are passed by reference.

What does filter(Boolean) do?

Passing `Boolean` as the callback converts each element to a boolean. Falsy values (`0`, `""`, `null`, `undefined`, `NaN`, `false`) evaluate to `false` and are excluded. Truthy values pass through. This is a quick way to clean arrays of nulls and empty strings, but be careful not to accidentally remove valid zeros or empty strings.

How do I filter and transform at the same time?

Chain `filter()` then [map()](/tutorials/programming-languages/javascript/how-to-use-the-javascript-array-map-method-today): `arr.filter(condition).map(transform)`. For a single-pass approach, use `reduce()` or `flatMap()`: `arr.flatMap(x => condition(x) ? [transform(x)] : [])`.

Can filter() return an empty array?

Yes. If no elements pass the test, `filter()` returns an empty array `[]`. It never returns `null` or `undefined`, making it safe to chain without null checks: `arr.filter(fn).map(fn2)` always works.

What is the difference between filter() and find()?

`filter()` returns all matching elements as an array. `find()` returns only the first matching element (or `undefined` if none match). Use `find()` when you need one result, `filter()` when you need all results. `find()` also stops iterating after the first match, making it more efficient for single-item lookups.

Conclusion

The filter() method is the standard tool for selecting elements from an array by condition. It creates a new array without modifying the original, making it safe for React state, Redux reducers, and any immutable data pattern. Combined with map() for transformation and reduce() for aggregation, filter() forms the core of declarative data processing in JavaScript. The critical points to remember are that it always returns a new array (never modifies the original), the Boolean shortcut removes falsy values, and find() is the better choice when you only need a single result.