Array Methods
map, filter, reduce, find, some, every — transform and query data without writing loops.
1. Introduction
JavaScript arrays come with powerful built-in methods that replace manual loops for most data transformations. Instead of writing "how to iterate", you describe "what to do with each item". These methods are the foundation of functional programming in JavaScript and are used constantly in real applications.
The most important are: map, filter, reduce, find, some, and every. Each accepts a callback function and returns a result.
2. Theory
2.1 map — transform every element
Returns a new array where each item is the result of running the callback. The original array is unchanged. Length of output always equals length of input.
const prices = [10, 25, 50, 100];
// Add 20% tax to each price
const withTax = prices.map(price => price * 1.2);
console.log(withTax); // [12, 30, 60, 120]
console.log(prices); // [10, 25, 50, 100] — unchanged
// Map array of objects to a simpler shape
const users = [
{ id: 1, name: 'Alice', age: 28 },
{ id: 2, name: 'Bob', age: 34 }
];
const names = users.map(user => user.name);
console.log(names); // ['Alice', 'Bob']
// Map with index
const numbered = ['a', 'b', 'c'].map((item, i) => `${i + 1}. ${item}`);
console.log(numbered); // ['1. a', '2. b', '3. c']
2.2 filter — keep matching elements
Returns a new array with only the elements for which the callback returns true. The original is unchanged. Length of output is less than or equal to length of input.
const scores = [45, 72, 88, 34, 91, 60];
const passing = scores.filter(score => score >= 60);
console.log(passing); // [72, 88, 91, 60]
// Filter objects
const products = [
{ name: 'Shirt', inStock: true, price: 29 },
{ name: 'Jeans', inStock: false, price: 59 },
{ name: 'Jacket', inStock: true, price: 89 }
];
const available = products.filter(p => p.inStock);
// [{ name: 'Shirt', ... }, { name: 'Jacket', ... }]
const affordable = products.filter(p => p.price < 50);
// [{ name: 'Shirt', inStock: true, price: 29 }]
// Chain map and filter
const availableNames = products
.filter(p => p.inStock)
.map(p => p.name);
console.log(availableNames); // ['Shirt', 'Jacket']
2.3 reduce — collapse to a single value
Iterates over the array, accumulating a single result. The callback receives the accumulator and the current item. The second argument to reduce is the initial value of the accumulator.
const nums = [1, 2, 3, 4, 5];
// Sum
const sum = nums.reduce((acc, n) => acc + n, 0);
console.log(sum); // 15
// Product
const product = nums.reduce((acc, n) => acc * n, 1);
console.log(product); // 120
// Find max value
const max = nums.reduce((acc, n) => (n > acc ? n : acc), -Infinity);
console.log(max); // 5
// Count occurrences
const words = ['cat', 'dog', 'cat', 'bird', 'dog', 'cat'];
const counts = words.reduce((acc, word) => {
acc[word] = (acc[word] ?? 0) + 1;
return acc;
}, {});
console.log(counts); // { cat: 3, dog: 2, bird: 1 }
// Group by property
const people = [
{ name: 'Alice', dept: 'Eng' },
{ name: 'Bob', dept: 'HR' },
{ name: 'Carol', dept: 'Eng' }
];
const byDept = people.reduce((acc, person) => {
if (!acc[person.dept]) acc[person.dept] = [];
acc[person.dept].push(person.name);
return acc;
}, {});
// { Eng: ['Alice', 'Carol'], HR: ['Bob'] }
2.4 find and findIndex
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Carol' }
];
// find — returns the first matching ELEMENT, or undefined
const user = users.find(u => u.id === 2);
console.log(user); // { id: 2, name: 'Bob' }
const missing = users.find(u => u.id === 99);
console.log(missing); // undefined
// findIndex — returns the first matching INDEX, or -1
const idx = users.findIndex(u => u.name === 'Carol');
console.log(idx); // 2
2.5 some and every
const ages = [15, 22, 18, 30, 16];
// some — true if AT LEAST ONE element passes
const hasAdult = ages.some(age => age >= 18);
console.log(hasAdult); // true
// every — true if ALL elements pass
const allAdults = ages.every(age => age >= 18);
console.log(allAdults); // false (15 and 16 fail)
// Practical uses
const cart = [
{ name: 'Shirt', inStock: true },
{ name: 'Hat', inStock: true }
];
const canCheckout = cart.every(item => item.inStock);
// true — safe to proceed
const hasOutOfStock = cart.some(item => !item.inStock);
// false — no out-of-stock items
2.6 flat and flatMap
// flat — flatten nested arrays
const nested = [1, [2, 3], [4, [5, 6]]];
console.log(nested.flat()); // [1, 2, 3, 4, [5, 6]] — one level
console.log(nested.flat(2)); // [1, 2, 3, 4, 5, 6] — two levels
console.log(nested.flat(Infinity)); // fully flatten
// flatMap — map then flat (one level)
const sentences = ['Hello world', 'foo bar'];
const allWords = sentences.flatMap(s => s.split(' '));
console.log(allWords); // ['Hello', 'world', 'foo', 'bar']
2.7 Chaining methods
// Real-world pipeline: filter → sort → map → join
const products = [
{ name: 'Widget A', price: 15, inStock: true },
{ name: 'Widget B', price: 45, inStock: false },
{ name: 'Widget C', price: 25, inStock: true },
{ name: 'Widget D', price: 10, inStock: true }
];
const result = products
.filter(p => p.inStock) // only in-stock
.sort((a, b) => a.price - b.price) // cheapest first
.map(p => `${p.name} — $${p.price}`) // format as strings
.join('\n'); // combine
console.log(result);
// "Widget D — $10
// Widget A — $15
// Widget C — $25"
2.8 forEach vs map — key difference
// forEach — for side effects, returns undefined
const result1 = [1,2,3].forEach(n => n * 2);
console.log(result1); // undefined — forEach returns nothing
// map — for transformations, returns new array
const result2 = [1,2,3].map(n => n * 2);
console.log(result2); // [2, 4, 6]
// Rule: if you're building a new array → use map
// if you're just doing something (logging, DOM update) → use forEach
3. Real World Example
// E-commerce dashboard — calculate stats from order data
const orders = [
{ id: 1, status: 'completed', amount: 120, items: 3 },
{ id: 2, status: 'pending', amount: 85, items: 1 },
{ id: 3, status: 'completed', amount: 240, items: 5 },
{ id: 4, status: 'cancelled', amount: 60, items: 2 },
{ id: 5, status: 'completed', amount: 195, items: 4 }
];
const completed = orders.filter(o => o.status === 'completed');
const revenue = completed.reduce((sum, o) => sum + o.amount, 0);
const avgOrder = revenue / completed.length;
const totalItems= completed.reduce((sum, o) => sum + o.items, 0);
const topOrder = completed.reduce((max, o) => o.amount > max.amount ? o : max);
console.log(`Completed orders: ${completed.length}`); // 3
console.log(`Total revenue: $${revenue}`); // $555
console.log(`Average order: $${avgOrder.toFixed(2)}`); // $185.00
console.log(`Items sold: ${totalItems}`); // 12
console.log(`Top order: #${topOrder.id} ($${topOrder.amount})`); // #3 ($240)
4. Code Example
<input id="search" placeholder="Search products">
<select id="sort">
<option value="name">Name</option>
<option value="price">Price</option>
</select>
<div id="product-list"></div>
<script>
const products = [
{ name: 'Laptop', price: 999, category: 'Electronics' },
{ name: 'Phone', price: 699, category: 'Electronics' },
{ name: 'Desk', price: 299, category: 'Furniture' },
{ name: 'Chair', price: 199, category: 'Furniture' },
{ name: 'Monitor', price: 399, category: 'Electronics' }
];
const searchInput = document.querySelector('#search');
const sortSelect = document.querySelector('#sort');
const container = document.querySelector('#product-list');
function render() {
const query = searchInput.value.toLowerCase();
const sortKey = sortSelect.value;
const visible = products
.filter(p => p.name.toLowerCase().includes(query))
.sort((a, b) => sortKey === 'price'
? a.price - b.price
: a.name.localeCompare(b.name));
container.innerHTML = '';
visible.forEach(p => {
const div = document.createElement('div');
div.textContent = `${p.name} — $${p.price} (${p.category})`;
container.appendChild(div);
});
}
searchInput.addEventListener('input', render);
sortSelect.addEventListener('change', render);
render(); // initial
</script>
5. Code Breakdown
Chained filter → sort
filter runs first, producing a smaller array of matching products. sort then runs on that smaller array. Neither modifies the original products array — so the full list is always available for the next search.
localeCompare for string sorting
a.name.localeCompare(b.name) returns negative/zero/positive — exactly what sort's comparator expects — and correctly handles locale-specific characters.
Re-render on input/change
Every keystroke and every sort change calls render(). The array pipeline recomputes from the original data each time, which is simple and correct. For very large datasets you would add debouncing.
6. Common Mistakes
Mistake 1 — Forgetting reduce's initial value
// Bug — no initial value, crashes on empty array
[].reduce((acc, n) => acc + n); // TypeError!
// Fix — always provide the initial value
[].reduce((acc, n) => acc + n, 0); // 0 — safe
Mistake 2 — Using map when forEach is appropriate (and vice versa)
// Bad — using map just for a side effect (wastes memory creating array)
products.map(p => console.log(p.name));
// Good — forEach for side effects
products.forEach(p => console.log(p.name));
// Bad — using forEach when you need a new array
const names = [];
products.forEach(p => names.push(p.name)); // verbose
// Good — map for transformation
const names = products.map(p => p.name);
Mistake 3 — Mutating the original in map/filter
// Bug — mutates the original objects
const withTax = products.map(p => {
p.price *= 1.2; // modifies the ORIGINAL object!
return p;
});
// Fix — create new objects with spread
const withTax = products.map(p => ({ ...p, price: p.price * 1.2 }));
Mistake 4 — Expecting find to return all matches
// Bug — find returns only the FIRST match
const items = [1, 2, 3, 2, 1];
const result = items.find(n => n === 2);
console.log(result); // 2 — just the value, not [2, 2]
// Fix — use filter for all matches
const all = items.filter(n => n === 2); // [2, 2]
7. Best Practices
- Use map for transformations, filter for selection, reduce for aggregation — pick the right tool.
- Always provide an initial value to reduce — avoids runtime errors on empty arrays.
- Don't mutate objects inside map — return new objects with spread
{ ...item, newProp }. - Chain methods for readable pipelines — filter → sort → map reads like English.
- Use some/every instead of filter(...).length for existence checks — they short-circuit (stop early).
- Keep callbacks short — if a map/filter callback is more than one expression, extract a named function.
8. Practice Exercise
- Given an array of numbers, use
mapto square each one, thenfilterto keep only values above 20, thenreduceto sum the remaining values. - Given an array of student objects
{ name, grade }, usefilterto get students with grade >= 60, thenmapto extract names, thensortalphabetically. - Use
reduceto count how many times each word appears in:['the', 'cat', 'sat', 'on', 'the', 'mat', 'the'].
9. Assignment
Build a "Job Board" filtering interface.
- Start with an array of 8 job objects:
{ title, company, location, salary, remote, type }(type: 'full-time'/'part-time'/'contract'). - Add filter controls: search by title/company text, filter by type (dropdown), filter by remote (checkbox), filter by minimum salary (number input).
- Display matching jobs in cards. Show the count of results.
- Use
filterto apply all active filters,mapto build the cards. - Add a "Sort by Salary" button that sorts ascending/descending on each click.
Deliverable: One HTML file.
10. Interview Questions
- What is the difference between map and forEach?
map returns a new array with the transformed elements — used for data transformation. forEach returns undefined — used only for side effects like logging or DOM updates. Both iterate every element. - What does filter return when no elements match?
An empty array [] — never null or undefined. This makes it safe to chain further methods without null checks. - What is reduce and when do you use it?
reduce collapses an array into a single value by running a callback with an accumulator. Use it to compute sums, build objects, count occurrences, or group data — any time you need one output from many inputs. - What is the difference between find and filter?
find returns the first matching element (or undefined). filter returns a new array of all matching elements (or []). Use find when you want one item, filter when you want multiple. - What is the benefit of some over filter for existence checks?
some short-circuits — it stops iterating as soon as it finds the first match. filter always processes every element. On a 10,000-element array, some can be much faster when the match appears early.
11. Additional Resources
- MDN — Array.prototype.map()
- MDN — Array.prototype.filter()
- MDN — Array.prototype.reduce()
- javascript.info — Array methods — comprehensive examples