JavaScript filter() Method: Filter Arrays Like a Pro
Master the JavaScript filter() method to create new arrays with elements that pass a test. Learn syntax, examples, and advanced filtering techniques.
JavaScript filter() Method: Filter Arrays Like a Pro
The filter()
method creates a new array with all elements that pass a test implemented by the provided function. It's one of the most powerful array methods for data manipulation in JavaScript.
Understanding filter()
The filter()
method tests each element against a condition and returns a new array containing only the elements that pass the test.
// Basic syntax
const newArray = array.filter(function (element, index, array) {
// Return true to keep the element, false to filter it out
return condition;
});
// Arrow function syntax
const newArray = array.filter((element, index, array) => condition);
Basic Examples
Filtering Numbers
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Get even numbers
const evens = numbers.filter((num) => num % 2 === 0);
console.log(evens); // [2, 4, 6, 8, 10]
// Get odd numbers
const odds = numbers.filter((num) => num % 2 !== 0);
console.log(odds); // [1, 3, 5, 7, 9]
// Numbers greater than 5
const greaterThanFive = numbers.filter((num) => num > 5);
console.log(greaterThanFive); // [6, 7, 8, 9, 10]
// Numbers in range
const inRange = numbers.filter((num) => num >= 3 && num <= 7);
console.log(inRange); // [3, 4, 5, 6, 7]
Filtering Strings
const words = ['apple', 'banana', 'grape', 'watermelon', 'kiwi'];
// Words with more than 5 characters
const longWords = words.filter((word) => word.length > 5);
console.log(longWords); // ['banana', 'watermelon']
// Words containing 'a'
const wordsWithA = words.filter((word) => word.includes('a'));
console.log(wordsWithA); // ['apple', 'banana', 'grape', 'watermelon']
// Words starting with specific letter
const wordsStartingWithG = words.filter((word) => word.startsWith('g'));
console.log(wordsStartingWithG); // ['grape']
Filtering Objects
Basic Object Filtering
const users = [
{ id: 1, name: 'John', age: 30, active: true },
{ id: 2, name: 'Jane', age: 25, active: false },
{ id: 3, name: 'Bob', age: 35, active: true },
{ id: 4, name: 'Alice', age: 28, active: true },
];
// Active users
const activeUsers = users.filter((user) => user.active);
console.log(activeUsers); // John, Bob, Alice
// Users over 30
const usersOver30 = users.filter((user) => user.age > 30);
console.log(usersOver30); // Bob
// Complex conditions
const qualifiedUsers = users.filter(
(user) => user.active && user.age >= 28 && user.age <= 35
);
console.log(qualifiedUsers); // John, Bob, Alice
Nested Object Filtering
const products = [
{
id: 1,
name: 'Laptop',
price: 999,
categories: ['electronics', 'computers'],
inStock: true,
},
{
id: 2,
name: 'Mouse',
price: 29,
categories: ['electronics', 'accessories'],
inStock: false,
},
{
id: 3,
name: 'Book',
price: 15,
categories: ['education'],
inStock: true,
},
];
// Products in stock
const availableProducts = products.filter((product) => product.inStock);
// Products in specific category
const electronics = products.filter((product) =>
product.categories.includes('electronics')
);
// Products within price range
const affordableProducts = products.filter(
(product) => product.price >= 10 && product.price <= 50
);
// Multiple conditions
const filteredProducts = products.filter(
(product) =>
product.inStock &&
product.price < 100 &&
product.categories.includes('electronics')
);
Using Index and Array Parameters
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Filter based on index (get even-indexed elements)
const evenIndexed = numbers.filter((num, index) => index % 2 === 0);
console.log(evenIndexed); // [1, 3, 5, 7, 9]
// Filter based on position
const firstHalf = numbers.filter(
(num, index, array) => index < array.length / 2
);
console.log(firstHalf); // [1, 2, 3, 4, 5]
// Remove first and last elements
const middleElements = numbers.filter(
(num, index, array) => index !== 0 && index !== array.length - 1
);
console.log(middleElements); // [2, 3, 4, 5, 6, 7, 8, 9]
Chaining filter() with Other Methods
filter() and map()
const users = [
{ name: 'John', age: 30, role: 'admin' },
{ name: 'Jane', age: 25, role: 'user' },
{ name: 'Bob', age: 35, role: 'admin' },
{ name: 'Alice', age: 28, role: 'user' },
];
// Get names of admin users
const adminNames = users
.filter((user) => user.role === 'admin')
.map((user) => user.name);
console.log(adminNames); // ['John', 'Bob']
// Transform filtered data
const youngUserProfiles = users
.filter((user) => user.age < 30)
.map((user) => ({
displayName: user.name.toUpperCase(),
ageGroup: 'young',
...user,
}));
filter(), map(), and reduce()
const orders = [
{ id: 1, product: 'Laptop', price: 999, status: 'delivered' },
{ id: 2, product: 'Mouse', price: 29, status: 'pending' },
{ id: 3, product: 'Keyboard', price: 79, status: 'delivered' },
{ id: 4, product: 'Monitor', price: 299, status: 'cancelled' },
];
// Calculate total of delivered orders
const deliveredTotal = orders
.filter((order) => order.status === 'delivered')
.map((order) => order.price)
.reduce((sum, price) => sum + price, 0);
console.log(deliveredTotal); // 1078
// Get summary of pending orders
const pendingSummary = orders
.filter((order) => order.status === 'pending')
.map((order) => ({
product: order.product,
price: order.price,
}))
.reduce(
(summary, order) => {
summary.count++;
summary.total += order.price;
summary.items.push(order.product);
return summary;
},
{ count: 0, total: 0, items: [] }
);
Practical Use Cases
Search Functionality
function searchProducts(products, searchTerm) {
const term = searchTerm.toLowerCase();
return products.filter(
(product) =>
product.name.toLowerCase().includes(term) ||
product.description.toLowerCase().includes(term) ||
product.tags.some((tag) => tag.toLowerCase().includes(term))
);
}
const products = [
{
name: 'iPhone',
description: 'Smartphone by Apple',
tags: ['mobile', 'phone'],
},
{
name: 'MacBook',
description: 'Laptop by Apple',
tags: ['computer', 'laptop'],
},
{
name: 'AirPods',
description: 'Wireless earphones',
tags: ['audio', 'wireless'],
},
];
console.log(searchProducts(products, 'phone'));
// Returns iPhone and AirPods (earphones)
Data Validation
// Filter valid email addresses
function filterValidEmails(emails) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emails.filter((email) => emailRegex.test(email));
}
const emails = [
'valid@email.com',
'invalid.email',
'another@valid.email',
'not-an-email',
'test@domain.org',
];
console.log(filterValidEmails(emails));
// ['valid@email.com', 'another@valid.email', 'test@domain.org']
// Filter valid data entries
function filterValidEntries(entries) {
return entries.filter((entry) => {
return (
entry != null && entry.id && entry.name && entry.name.trim().length > 0
);
});
}
Permission-Based Filtering
class ContentFilter {
constructor(userRole) {
this.userRole = userRole;
}
filterByPermission(items) {
const permissions = {
guest: ['public'],
user: ['public', 'user'],
admin: ['public', 'user', 'admin'],
};
const allowedTypes = permissions[this.userRole] || [];
return items.filter((item) => allowedTypes.includes(item.visibility));
}
}
const content = [
{ title: 'Public Post', visibility: 'public' },
{ title: 'User Post', visibility: 'user' },
{ title: 'Admin Post', visibility: 'admin' },
];
const guestFilter = new ContentFilter('guest');
console.log(guestFilter.filterByPermission(content));
// Only public post
Advanced Filtering Techniques
Dynamic Filter Functions
// Filter builder
function createFilter(criteria) {
return function (item) {
return Object.entries(criteria).every(([key, value]) => {
if (typeof value === 'function') {
return value(item[key]);
}
return item[key] === value;
});
};
}
const users = [
{ name: 'John', age: 30, city: 'New York' },
{ name: 'Jane', age: 25, city: 'Boston' },
{ name: 'Bob', age: 35, city: 'New York' },
];
// Create custom filters
const newYorkAdults = createFilter({
city: 'New York',
age: (age) => age >= 18,
});
console.log(users.filter(newYorkAdults));
// John and Bob
Combining Multiple Filters
// Filter composition
function combineFilters(...filters) {
return (item) => filters.every((filter) => filter(item));
}
function orFilters(...filters) {
return (item) => filters.some((filter) => filter(item));
}
const products = [
{ name: 'Laptop', price: 999, category: 'electronics', brand: 'Apple' },
{ name: 'Shirt', price: 29, category: 'clothing', brand: 'Nike' },
{ name: 'Phone', price: 699, category: 'electronics', brand: 'Samsung' },
];
const isElectronics = (product) => product.category === 'electronics';
const isPremium = (product) => product.price > 500;
const isApple = (product) => product.brand === 'Apple';
// AND combination
const premiumElectronics = combineFilters(isElectronics, isPremium);
console.log(products.filter(premiumElectronics));
// Laptop and Phone
// OR combination
const appleOrPremium = orFilters(isApple, isPremium);
console.log(products.filter(appleOrPremium));
// Laptop and Phone
Memoized Filtering
// Cache filter results for performance
function memoizedFilter(filterFn) {
const cache = new Map();
return function (array) {
const key = JSON.stringify(array);
if (cache.has(key)) {
return cache.get(key);
}
const result = array.filter(filterFn);
cache.set(key, result);
return result;
};
}
const expensiveFilter = memoizedFilter((item) => {
// Simulate expensive computation
console.log('Computing...');
return item.value > 100;
});
const data = [{ value: 50 }, { value: 150 }, { value: 200 }];
console.log(expensiveFilter(data)); // Computes
console.log(expensiveFilter(data)); // Returns cached result
Removing Duplicates
// Using filter with indexOf
const numbers = [1, 2, 2, 3, 3, 3, 4, 5, 5];
const unique = numbers.filter(
(num, index, array) => array.indexOf(num) === index
);
console.log(unique); // [1, 2, 3, 4, 5]
// Remove duplicate objects
const users = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' },
{ id: 1, name: 'John' },
{ id: 3, name: 'Bob' },
];
const uniqueUsers = users.filter(
(user, index, array) => array.findIndex((u) => u.id === user.id) === index
);
console.log(uniqueUsers); // 3 unique users
// Using Set (alternative approach)
const uniqueWithSet = [...new Set(numbers)];
Performance Considerations
// Avoid multiple passes
const data = Array(10000)
.fill(0)
.map((_, i) => ({
id: i,
value: Math.random() * 1000,
active: Math.random() > 0.5,
}));
// Less efficient - multiple filter calls
console.time('multiple filters');
const result1 = data
.filter((item) => item.active)
.filter((item) => item.value > 500)
.filter((item) => item.id % 2 === 0);
console.timeEnd('multiple filters');
// More efficient - single filter
console.time('single filter');
const result2 = data.filter(
(item) => item.active && item.value > 500 && item.id % 2 === 0
);
console.timeEnd('single filter');
// Early return for complex filters
function complexFilter(item) {
if (!item.active) return false; // Early return
if (item.value <= 500) return false; // Early return
// Expensive computation only if necessary
return expensiveCheck(item);
}
Common Pitfalls and Solutions
Modifying Original Array
// Wrong - modifying during filter
const users = [
{ name: 'John', age: 30 },
{ name: 'Jane', age: 25 },
];
const filtered = users.filter((user) => {
user.processed = true; // Don't do this!
return user.age > 28;
});
// Right - keep filter pure
const filtered = users.filter((user) => user.age > 28);
const processed = filtered.map((user) => ({
...user,
processed: true,
}));
Falsy Values
const mixed = [0, 1, '', 'hello', false, true, null, undefined, NaN];
// Filter truthy values (careful with 0)
const truthy = mixed.filter(Boolean);
console.log(truthy); // [1, 'hello', true]
// Filter non-null/undefined
const defined = mixed.filter((item) => item != null);
console.log(defined); // [0, 1, '', 'hello', false, true]
// Filter numbers including 0
const numbers = mixed.filter(
(item) => typeof item === 'number' && !isNaN(item)
);
console.log(numbers); // [0, 1]
Empty Arrays
const empty = [];
const filtered = empty.filter((item) => item > 0);
console.log(filtered); // [] - No error, returns empty array
// Check if any elements pass
function hasMatchingElements(array, predicate) {
return array.filter(predicate).length > 0;
// Or use some() for better performance
// return array.some(predicate);
}
filter() vs Other Methods
filter() vs find()
const users = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' },
{ id: 3, name: 'Bob' },
];
// filter - returns array of all matches
const johnsFilter = users.filter((user) => user.name.includes('John'));
console.log(johnsFilter); // [{ id: 1, name: 'John' }]
// find - returns first match
const johnFind = users.find((user) => user.name.includes('John'));
console.log(johnFind); // { id: 1, name: 'John' }
filter() vs reduce()
const numbers = [1, 2, 3, 4, 5];
// filter - creates new array with matching elements
const evens = numbers.filter((n) => n % 2 === 0);
// reduce - can filter and transform in one pass
const evensDoubled = numbers.reduce((acc, n) => {
if (n % 2 === 0) {
acc.push(n * 2);
}
return acc;
}, []);
Best Practices
-
Keep filter functions pure - Don't modify elements
-
Use descriptive predicate names
const isActive = (user) => user.active; const activeUsers = users.filter(isActive);
-
Combine conditions in single filter for performance
-
Consider using
some()
orevery()
for boolean checks -
Use filter for its purpose - creating subset arrays
-
Handle edge cases like null, undefined, empty arrays
-
Chain responsibly - balance readability and performance
Conclusion
The filter()
method is essential for creating new arrays based on conditions. Key takeaways:
- Creates a new array with elements that pass a test
- Doesn't modify the original array
- Can be chained with other array methods
- Perfect for search, validation, and data filtering
- Always returns an array (empty if no matches)
Master filter()
to write cleaner, more functional JavaScript code for data manipulation!