JavaScript Array Iteration Methods
Array iteration methods are functions built into every JavaScript array that let you loop over its elements without writing a manual for loop. Instead of tracking an index yourself, you pass a callback function, and the array calls that function once for each element. This makes code shorter, less error-prone, and easier to read because the intent (“transform every item”, “keep only some items”, “combine everything into one value”) is stated directly by the method name you choose.
Overview: How Iteration Methods Work
Every iteration method follows the same basic contract: you call it on an array, you give it a callback function, and the method invokes that callback once for each element in order, passing three arguments to it: the current element, its index, and the full array being iterated. What differs between the methods is what they do with the callback’s return value and what they hand back to you.
forEach() ignores the callback’s return value entirely and always returns undefined — it exists purely for side effects like logging or pushing into another structure. map() collects every return value into a brand-new array of the same length. filter() treats the return value as a boolean test and keeps only the elements where it was truthy, producing a new (possibly shorter) array. reduce() is the most general: it carries an accumulator value from one call to the next, letting you fold the whole array down into a single result — a sum, an object, another array, anything. find() and findIndex() stop as soon as the callback returns true and give you the element (or its index); some() and every() also stop early, testing whether at least one or all elements satisfy a condition.
Internally, these methods are just wrappers around a loop. The JavaScript engine walks the array’s indices from 0 to length - 1, calling your callback at each step with this bound according to a second, optional argument you can pass to most of these methods. Two details matter a lot in practice: none of map, filter, find, some, or every mutate the original array — they read it and return something new (or nothing) — and all of them skip empty slots in sparse arrays (arrays with holes), which is a subtle difference from a plain for loop.
Syntax
array.forEach((element, index, array) => { /* side effect */ });
const mapped = array.map((element, index, array) => newValue);
const filtered = array.filter((element, index, array) => condition);
const result = array.reduce((accumulator, element, index, array) => newAccumulator, initialValue);
const found = array.find((element, index, array) => condition);
const foundIdx = array.findIndex((element, index, array) => condition);
const any = array.some((element, index, array) => condition);
const all = array.every((element, index, array) => condition);
const flat = array.flatMap((element, index, array) => newValueOrArray);
| Method | Returns | Stops early? | Typical use |
|---|---|---|---|
forEach |
undefined |
No | Side effects (logging, DOM updates) |
map |
New array, same length | No | Transform each element |
filter |
New array, same or shorter | No | Keep matching elements |
reduce |
Any single value | No | Sums, grouping, building objects |
find |
Element or undefined |
Yes | Get the first match |
findIndex |
Index or -1 |
Yes | Get the position of the first match |
some |
Boolean | Yes | “Does at least one match?” |
every |
Boolean | Yes | “Do all match?” |
flatMap |
New, flattened array | No | Map then flatten one level |
Examples
Example 1: forEach vs. map
const numbers = [1, 2, 3, 4, 5];
let sumForEach = 0;
numbers.forEach(n => {
sumForEach += n;
});
console.log(sumForEach);
const doubled = numbers.map(n => n * 2);
console.log(doubled);
Output:
15
[ 2, 4, 6, 8, 10 ]
forEach is used here purely to accumulate a side effect (adding to sumForEach); its own return value is thrown away, which is why it can’t be chained. map, in contrast, produces a brand-new array — the original numbers array is untouched — because its whole purpose is to hand back a transformed value for every element.
Example 2: Filtering and reducing a shopping cart
const cart = [
{ name: 'Keyboard', price: 45, inStock: true },
{ name: 'Monitor', price: 199, inStock: false },
{ name: 'Mouse', price: 25, inStock: true },
{ name: 'Webcam', price: 60, inStock: true },
];
const availableItems = cart.filter(item => item.inStock);
console.log(availableItems.map(item => item.name));
const total = availableItems.reduce((sum, item) => sum + item.price, 0);
console.log(`Total: $${total}`);
Output:
[ 'Keyboard', 'Mouse', 'Webcam' ]
Total: $130
This is a realistic chain: filter narrows the array down to only the items that are in stock, then reduce folds that shorter array into a single total by carrying a running sum starting from the initial value 0. Chaining filter and reduce like this is extremely common — filter first to shrink the working set, then reduce to combine it.
Example 3: Searching and testing with find, some, every, and flatMap
const users = [
{ id: 1, name: 'Ana', age: 17 },
{ id: 2, name: 'Ben', age: 22 },
{ id: 3, name: 'Cleo', age: 19 },
];
const adult = users.find(u => u.age >= 18);
console.log(adult);
const index = users.findIndex(u => u.name === 'Cleo');
console.log(index);
console.log(users.some(u => u.age < 18));
console.log(users.every(u => u.age >= 18));
const tags = [['js', 'web'], ['css'], ['html', 'dom']];
console.log(tags.flatMap(t => t));
Output:
{ id: 2, name: 'Ben', age: 22 }
2
true
false
[ 'js', 'web', 'css', 'html', 'dom' ]
find stops at the very first user whose age is 18 or older and returns that whole object — not just true. findIndex does the same search but returns a position instead. some confirms at least one user is a minor, while every confirms not all users are adults. flatMap maps each nested array to itself and then flattens the results by one level, which is exactly why it’s the idiomatic tool for “map, then flatten” instead of calling .map().flat() separately.
Under the Hood: How reduce() Actually Works
It helps to see that reduce is not magic — it’s a small loop that carries a value forward. Here is a simplified reimplementation that behaves the same way as the built-in version:
function myReduce(array, callback, initialValue) {
let accumulator = initialValue;
let startIndex = 0;
if (accumulator === undefined) {
accumulator = array[0];
startIndex = 1;
}
for (let i = startIndex; i < array.length; i++) {
accumulator = callback(accumulator, array[i], i, array);
}
return accumulator;
}
const result = myReduce([1, 2, 3, 4], (acc, n) => acc + n, 0);
console.log(result);
Output:
10
On each pass through the loop, the engine calls your callback with whatever the previous callback call returned, which is why it’s called an “accumulator” — it accumulates state across iterations. If you don’t supply an initial value, the real reduce uses the first element as the starting accumulator and begins looping from index 1 instead of 0, exactly as shown above. Every other iteration method (map, filter, forEach, and so on) can actually be written in terms of a loop just like this one — they only differ in what they do with the callback’s return value and what they hand back at the end.
Common Mistakes
Mistake 1: Forgetting to return from a map() callback with braces
An arrow function with a block body (curly braces) does not implicitly return anything — you must write return explicitly. Forgetting it is one of the most common bugs with map:
const doubled = [1, 2, 3].map(n => { n * 2 });
console.log(doubled);
Output:
[ undefined, undefined, undefined ]
The code is perfectly valid JavaScript — { n * 2 } is parsed as a block statement, not an object, and since nothing is returned from that block, every call yields undefined. Fix it by either adding return or dropping the braces entirely for an implicit return:
const doubled = [1, 2, 3].map(n => n * 2);
console.log(doubled);
Output:
[ 2, 4, 6 ]
Mistake 2: Calling reduce() on an empty array with no initial value
If you omit the initial value and the array is empty, there is no first element to use as a starting accumulator, and reduce throws instead of silently returning something like 0 or undefined:
try {
const total = [].reduce((acc, n) => acc + n);
console.log(total);
} catch (error) {
console.log(error.message);
}
Output:
Reduce of empty array with no initial value
The fix is simple and should be a habit: always pass an explicit initial value (like 0 for sums or []/{} for building collections) so reduce has something safe to fall back on even when the array turns out to be empty.
Best Practices
- Reach for the most specific method available: use
findinstead offilter(...)[0], andsome/everyinstead of manually reducing to a boolean — they read clearer and stop iterating as soon as the answer is known. - Always pass an initial value to
reduce, even when you’re fairly sure the array won’t be empty — it protects against a runtime crash and makes the accumulator’s type explicit at a glance. - Don’t use
mapwhen you only want side effects and don’t need the returned array — useforEachso the intent (“I’m not building anything new here”) is clear to readers. - Avoid mutating the array or outside state inside a
map/filtercallback; keep these pure so the result is predictable and the original array stays untouched. - Chain iteration methods (
filterthenmapthenreduce) instead of writing one large manual loop — each method documents one step of the transformation. - Prefer
flatMapover a separate.map().flat()call when mapping each element to zero, one, or several values — it’s both clearer and slightly more efficient since it avoids building an intermediate nested array. - Remember that
forEachcannot be stopped early (nobreak) and always returnsundefined— if you need early exit, use afor...ofloop or a method likesome/findthat naturally short-circuits.
Practice Exercises
- Given
const prices = [12, 45, 8, 99, 23];, usefilterto get all prices over20, then usereduceto find their total. - Given an array of student objects like
{ name: 'Sam', score: 72 }, usemapto produce a new array of just the names of students who scored 60 or above (you’ll need to combinefilterandmap). - Write a function
hasDuplicate(array)that usessometogether withindexOforfindIndexto returntrueif any value appears more than once in the array, andfalseotherwise.
Summary
- Iteration methods call a callback once per element, passing
(element, index, array), so you rarely need to manage an index manually. forEachis for side effects and always returnsundefined;maptransforms every element into a new array of the same length.filterkeeps elements that pass a test;reducefolds the whole array into one accumulated value, and should always get an initial value.find/findIndexandsome/everyall stop iterating as soon as the answer is determined, unlikemap,filter, andforEach.flatMapmaps and flattens one level in a single pass, avoiding a separate.flat()call.- None of these methods (except when you explicitly mutate inside the callback) change the original array — they read it and return something new.
