JavaScript BasicsFeatured

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.

By JavaScriptDoc Team
filterarraysarray methodsfunctional programmingjavascript

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

  1. Keep filter functions pure - Don't modify elements

  2. Use descriptive predicate names

    const isActive = (user) => user.active;
    const activeUsers = users.filter(isActive);
    
  3. Combine conditions in single filter for performance

  4. Consider using some() or every() for boolean checks

  5. Use filter for its purpose - creating subset arrays

  6. Handle edge cases like null, undefined, empty arrays

  7. 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!