Array methods: map, filter and reduce
Objects, arrays and data16 min readLesson 2 of 4
2.1 Methods as data pipelines
An array method takes a callback and decides when to call it. You describe the transformation for one item, and the method handles the iteration. This separates the question "what happens to each item" from the mechanics of walking the array.
The three central methods are map, filter and reduce. map transforms every item and returns a new array of the same length. filter keeps the items whose callback returns a truthy value and returns a shorter array. reduce folds every item into one accumulated value, which can be a number, a string, an object or a new array.
None of the three mutates the original array. That property makes them safe to compose: chain a filter into a map into another filter, and the original data stays usable for a second report.
2.2 forEach for side effects
forEach visits each item and runs the callback for its effect. It returns undefined, so it cannot be chained and it is not the tool for producing a new array. Use it when the job is logging, saving or rendering one item at a time.
The callback receives the value, the index and the array. Most code needs only the value, and writing fewer parameters keeps the intent clear. If the callback starts building a result, that result is usually a sign that map, filter or reduce was the better tool.
const prices = [20, 60, 450];
const withGst = prices.map((price) => Math.round(price * 1.18));
console.log(JSON.stringify(withGst));
prices.forEach((price, index) => {
console.log(`${index}: ${price}`);
});Output
[24,71,531] 0: 20 1: 60 2: 450
2.3 map and the one-to-one rule
map returns a new array with one output for every input. The callback's return value becomes the new item. A common mistake is using map with a callback that returns nothing; the result is an array of undefined values with the right length, which looks plausible until it is read.
Keep the callback small and pure. A callback that changes the original item while also returning a new value mixes two jobs and makes the output hard to predict. Build the new value from the old one, return it, and let map assemble the array.
const users = [
{ name: "Aarav", active: true },
{ name: "Meera", active: false },
{ name: "Sana", active: true },
];
const activeNames = users.filter((user) => user.active).map((user) => user.name);
const missing = users.find((user) => user.name === "Kabir");
console.log(JSON.stringify(activeNames));
console.log(missing);
console.log(users.some((user) => !user.active));
console.log(users.every((user) => user.name.length > 0));Output
["Aarav","Sana"] undefined true true
2.4 filter, find and the predicates
filter returns all items whose predicate is truthy. find returns the first match or undefined. some returns true when at least one item matches, and every returns true only when all items match. includes checks for a value rather than a predicate.
These methods answer different questions, and choosing the right one keeps the code honest. filter for a new list, find for one item, some for existence and every for a universal rule. The callback in each case is a predicate: it returns a boolean, and the method attaches the iteration logic.
2.5 reduce and the accumulator
reduce takes a reducer callback and an initial value. On each pass it receives the accumulator and the current item, and it returns the new accumulator. After the last item, that accumulator is the result. The initial value states the type of the result, so choose it deliberately.
A sum starts at 0, a product at 1, a string at "", a count object at {}. When the initial value is omitted, reduce uses the first array item and starts from the second, which produces confusing results on typed accumulators and throws on an empty array. Always pass an initial value in beginner code.
const orders = [
{ item: "pen", quantity: 10, price: 20 },
{ item: "book", quantity: 2, price: 60 },
];
const total = orders.reduce((sum, order) => sum + order.quantity * order.price, 0);
const count = orders.reduce((result, order) => {
result[order.item] = order.quantity;
return result;
}, {});
console.log(total);
console.log(JSON.stringify(count));Output
320
{"pen":10,"book":2}2.6 Chaining and readability
Each method returns an array or a value, so calls chain from left to right: filter a list, map the survivors into the shape a page needs, then reduce to a summary. Deep chains read like a sentence when each line starts at the same indent.
Chains that run longer than four calls, or contain inline functions larger than a few lines, are easier to read as named steps. A named intermediate array costs nothing at this level and gives the reader a checkpoint for debugging. Test the intermediate value before adding the next step.
Mutation breaks the chain's promise. A callback that pushes to an outer array or changes an item mixes a side effect into a pipeline, and the next method receives data that no longer matches the original. Keep pipeline callbacks pure.
2.7 sort and toSorted
sort mutates the original array and converts items to strings before comparing by default, so [10, 2, 1].sort() gives [1, 10, 2]. Always pass a comparator for numbers: (a, b) => a - b for ascending order and (a, b) => b - a for descending.
toSorted returns a new sorted array and leaves the original alone. When the data is shared, the copying version avoids a surprising change in another part of the page. The comparator receives two items and returns a negative number when the first should come first, a positive number when it should come second, and zero when order does not matter.
2.8 Method or loop?
Array methods are not a rule that bans loops. A method chain is clearest when the steps are transformations of a list. A plain loop is clearer when the body needs several statements, early exits, or work outside the array.
For example, a loop that searches two arrays at once or breaks on the first match is easier to read as a for loop with break. A method chain that calls map and filter on one list is easier to read as a chain. Reach for the shape that matches the job, and keep the callback small either way.
Performance rarely decides the choice for beginner-sized data. Both approaches visit each item once, and the difference is noise next to a network request or a DOM update. Readability wins.
2.9 Common mistakes
- Using map and forgetting to return from the callback.
- Using filter to find one item and receiving an array.
- Calling reduce without an initial value and failing on an empty array.
- Sorting numbers without a comparator and getting dictionary order.
- Mutating an object inside a map callback.
- Chaining a method that returns undefined, such as forEach.
Exam
Method questions usually show a short chain and ask for the printed array. Work left to right, write the intermediate array after each call, and check the callback's return value. When reduce is present, write the accumulator after every pass.
- Use map to double every number in an array.
- Use filter and find on the same list and explain the difference in the results.
- Use reduce to count how many times each word appears.
- Sort a list of numbers with and without a comparator.
