Array Methods: map, filter, reduce, forEach
These four array methods are essential for functional programming in JavaScript. They transform arrays without modifying the original.
forEach()
Executes a function for each array element:
const numbers = [1, 2, 3, 4, 5];
numbers.forEach(num => {
console.log(num);
});
// Output: 1, 2, 3, 4, 5
With Index and Array
const fruits = ["apple", "banana", "orange"];
fruits.forEach((fruit, index, array) => {
console.log(`${index}: ${fruit}`);
});
// Output:
// 0: apple
// 1: banana
// 2: orange
forEach vs for Loop
// forEach - functional style
numbers.forEach(num => console.log(num));
// for loop - imperative style
for (let i = 0; i < numbers.length; i++) {
console.log(numbers[i]);
}
// forEach can't break or return
map()
Transforms each element and returns a new array:
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
console.log(numbers); // [1, 2, 3, 4, 5] (unchanged)
Transforming Objects
const users = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
{ id: 3, name: "Charlie" }
];
const names = users.map(user => user.name);
console.log(names); // ["Alice", "Bob", "Charlie"]
Chaining map()
const numbers = [1, 2, 3, 4, 5];
const result = numbers
.map(num => num * 2)
.map(num => num + 1)
.map(num => num / 2);
console.log(result); // [1.5, 2.5, 3.5, 4.5, 5.5]
Converting Types
const strings = ["1", "2", "3", "4"];
const numbers = strings.map(str => parseInt(str));
console.log(numbers); // [1, 2, 3, 4]
const booleans = [1, 0, 1, 0].map(num => Boolean(num));
console.log(booleans); // [true, false, true, false]
filter()
Returns a new array with elements that pass a test:
const numbers = [1, 2, 3, 4, 5, 6];
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4, 6]
Filtering Objects
const users = [
{ id: 1, name: "Alice", active: true },
{ id: 2, name: "Bob", active: false },
{ id: 3, name: "Charlie", active: true }
];
const activeUsers = users.filter(user => user.active);
console.log(activeUsers);
// [{ id: 1, name: "Alice", active: true }, { id: 3, name: "Charlie", active: true }]
Removing Duplicates
const numbers = [1, 2, 2, 3, 3, 3, 4];
const unique = numbers.filter((num, index, arr) => arr.indexOf(num) === index);
console.log(unique); // [1, 2, 3, 4]
// Or with Set
const unique2 = [...new Set(numbers)];
console.log(unique2); // [1, 2, 3, 4]
Chaining filter()
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const result = numbers
.filter(num => num > 3)
.filter(num => num < 8)
.filter(num => num % 2 === 0);
console.log(result); // [4, 6]
reduce()
Reduces array to a single value:
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((accumulator, current) => {
return accumulator + current;
}, 0);
console.log(sum); // 15
Anatomy
array.reduce((accumulator, current, index, array) => {
// accumulator: accumulated value
// current: current element
// index: current index
// array: the array being reduced
}, initialValue);
Without Initial Value
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, num) => acc + num);
console.log(sum); // 15 (starts with first element)
Counting Occurrences
const fruits = ["apple", "banana", "apple", "orange", "banana", "apple"];
const counts = fruits.reduce((acc, fruit) => {
acc[fruit] = (acc[fruit] || 0) + 1;
return acc;
}, {});
console.log(counts);
// { apple: 3, banana: 2, orange: 1 }
Flattening Arrays
const nested = [[1, 2], [3, 4], [5, 6]];
const flat = nested.reduce((acc, arr) => acc.concat(arr), []);
console.log(flat); // [1, 2, 3, 4, 5, 6]
// Or with spread
const flat2 = nested.reduce((acc, arr) => [...acc, ...arr], []);
console.log(flat2); // [1, 2, 3, 4, 5, 6]
Building Objects
const users = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
{ id: 3, name: "Charlie" }
];
const userMap = users.reduce((acc, user) => {
acc[user.id] = user;
return acc;
}, {});
console.log(userMap);
// { 1: { id: 1, name: "Alice" }, 2: { id: 2, name: "Bob" }, ... }
Grouping by Property
const users = [
{ name: "Alice", role: "admin" },
{ name: "Bob", role: "user" },
{ name: "Charlie", role: "admin" }
];
const grouped = users.reduce((acc, user) => {
if (!acc[user.role]) {
acc[user.role] = [];
}
acc[user.role].push(user);
return acc;
}, {});
console.log(grouped);
// { admin: [Alice, Charlie], user: [Bob] }
Combining Methods
map + filter
const users = [
{ id: 1, name: "Alice", age: 30 },
{ id: 2, name: "Bob", age: 25 },
{ id: 3, name: "Charlie", age: 35 }
];
const adultNames = users
.filter(user => user.age >= 30)
.map(user => user.name);
console.log(adultNames); // ["Alice", "Charlie"]
filter + reduce
const numbers = [1, 2, 3, 4, 5, 6];
const sumOfEvens = numbers
.filter(num => num % 2 === 0)
.reduce((sum, num) => sum + num, 0);
console.log(sumOfEvens); // 12 (2 + 4 + 6)
map + reduce
const items = [
{ name: "apple", price: 1.5, quantity: 3 },
{ name: "banana", price: 0.5, quantity: 2 },
{ name: "orange", price: 2, quantity: 1 }
];
const total = items
.map(item => item.price * item.quantity)
.reduce((sum, price) => sum + price, 0);
console.log(total); // 7.5
All Three
const transactions = [
{ type: "income", amount: 1000 },
{ type: "expense", amount: 200 },
{ type: "income", amount: 500 },
{ type: "expense", amount: 100 }
];
const netIncome = transactions
.filter(t => t.type === "income")
.map(t => t.amount)
.reduce((sum, amount) => sum + amount, 0);
console.log(netIncome); // 1500
Practical Examples
Data Processing Pipeline
const rawData = [
{ id: 1, name: "Alice", score: 85 },
{ id: 2, name: "Bob", score: 92 },
{ id: 3, name: "Charlie", score: 78 },
{ id: 4, name: "David", score: 88 }
];
const report = rawData
.filter(student => student.score >= 80)
.map(student => ({
name: student.name,
grade: student.score >= 90 ? "A" : "B"
}))
.reduce((acc, student) => {
acc[student.grade] = (acc[student.grade] || 0) + 1;
return acc;
}, {});
console.log(report); // { A: 1, B: 3 }
Shopping Cart Total
const cart = [
{ product: "Laptop", price: 1000, quantity: 1 },
{ product: "Mouse", price: 25, quantity: 2 },
{ product: "Keyboard", price: 75, quantity: 1 }
];
const total = cart.reduce((sum, item) => sum + (item.price * item.quantity), 0);
console.log(total); // 1175
Search and Transform
const users = [
{ id: 1, name: "Alice", email: "[email protected]" },
{ id: 2, name: "Bob", email: "[email protected]" },
{ id: 3, name: "Charlie", email: "[email protected]" }
];
const searchResults = users
.filter(user => user.name.toLowerCase().includes("a"))
.map(user => ({ name: user.name, email: user.email }));
console.log(searchResults);
// [{ name: "Alice", email: "[email protected]" }, { name: "Charlie", email: "[email protected]" }]
Performance Tips
Use Appropriate Method
// Good - use forEach for side effects
numbers.forEach(num => console.log(num));
// Good - use map to transform
const doubled = numbers.map(num => num * 2);
// Good - use filter to select
const evens = numbers.filter(num => num % 2 === 0);
// Good - use reduce for aggregation
const sum = numbers.reduce((a, b) => a + b, 0);
Avoid Unnecessary Iterations
// Bad - multiple iterations
const result = numbers
.map(num => num * 2)
.filter(num => num > 5)
.map(num => num + 1);
// Better - combine operations
const result2 = numbers.reduce((acc, num) => {
const doubled = num * 2;
if (doubled > 5) {
acc.push(doubled + 1);
}
return acc;
}, []);
Summary
- forEach(): execute function for each element
- map(): transform elements, return new array
- filter(): select elements matching condition
- reduce(): aggregate to single value
- Chainable: combine methods for complex operations
- Immutable: don’t modify original array
- Functional: enables functional programming style
Related Resources
Official Documentation
- MDN: Array.prototype.forEach()
- MDN: Array.prototype.map()
- MDN: Array.prototype.filter()
- MDN: Array.prototype.reduce()
Next Steps
- Callbacks and Asynchronous JavaScript
- Promises: Creation, Chaining, Resolution
- Async/Await: Modern Asynchronous Programming
Comments