Map.prototype.forEach()
map.forEach(callbackFn, thisArg) The forEach() method calls a callback once for each key-value pair in the Map, in insertion order. The callback receives the value, key, and the Map itself as arguments.
Syntax
map.forEach(callbackFn, thisArg)
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
callbackFn | function | — | Function called for each key-value pair |
value | any | — | The current value |
key | any | — | The current key |
map | Map | — | The Map being iterated |
thisArg | any | undefined | Value to use as this |
Examples
Basic usage
const prices = new Map([
["apple", 1.5],
["banana", 0.75],
["orange", 2.0]
]);
prices.forEach((value, key) => {
console.log(`${key}: $${value}`);
});
// apple: $1.5
// banana: $0.75
// orange: $2.0
forEach() also accepts an optional thisArg parameter that sets the this value inside the callback. This matters when the callback is a method that relies on a specific context. Arrow functions ignore thisArg since they capture this lexically from the enclosing scope, so use a regular function expression when you need to pass a custom this.
Using thisArg
const multiplier = {
factor: 2
};
const nums = new Map([["a", 1], ["b", 2], ["c", 3]]);
nums.forEach(function(value, key) {
console.log(`${key}: ${value} x ${this.factor} = ${value * this.factor}`);
}, multiplier);
// a: 1 x 2 = 2
// b: 2 x 2 = 4
// c: 3 x 2 = 6
The callback does not need to use both key and value. When you only care about the values, you can accumulate a running total or build a result incrementally without referencing the key at all. The value parameter alone is enough for summation, averaging, or any reduction pattern.
Calculating totals
const orders = new Map([
["order1", 100],
["order2", 250],
["order3", 75]
]);
let total = 0;
orders.forEach((value) => {
total += value;
});
console.log(`Total: $${total}`); // Total: $425
You can also push entries into a different data structure during iteration. Converting a Map’s entries into an array of formatted strings is a common pattern when you need the data in a shape that works better for display, serialization, or downstream processing.
Transforming to another structure
const users = new Map([
[1, { name: "Alice" }],
[2, { name: "Bob" }],
[3, { name: "Carol" }]
]);
const names = [];
users.forEach((value, key) => {
names.push(`${key}: ${value.name}`);
});
console.log(names); // [1: Alice, 2: Bob, 3: Carol]
Beyond simple accumulation, forEach() works well for collecting entries that meet a condition. The callback can test each pair and selectively push matching keys or values into a result array, which is often cleaner than first converting to an array and then calling .filter().
Filtering during iteration
const scores = new Map([
["player1", 95],
["player2", 82],
["player3", 78],
["player4", 91]
]);
const highScorers = [];
scores.forEach((value, key) => {
if (value >= 90) {
highScorers.push(key);
}
});
console.log(highScorers); // [player1, player4]
forEach() can also mutate the Map it is iterating over. The third callback argument is the Map itself, so you can call map.set() to update values as you go. This works because the Map’s insertion-order iteration visits entries added during iteration, as described later in the behavior notes.
Modifying values in place
const inventory = new Map([
["apples", 10],
["bananas", 5],
["oranges", 8]
]);
inventory.forEach((value, key, map) => {
map.set(key, value * 2);
});
console.log([...inventory]);
// [[apples, 20], [bananas, 10], [oranges, 16]]
forEach() does not work well with async/await directly — it ignores the promise returned by an async callback and does not wait for it to settle. For sequential async iteration over a Map, a for...of loop is the correct tool, as shown next. Use forEach() only for synchronous side effects.
With async operations
const apiCalls = new Map([
["user", "/api/user/1"],
["posts", "/api/posts"],
["comments", "/api/comments"]
]);
async function fetchAll() {
const results = new Map();
for (const [key, url] of apiCalls) {
// Simulated fetch
results.set(key, { url, status: "loaded" });
}
return results;
}
fetchAll().then(data => console.log([...data.entries()]));
Behavior notes
- Iteration order follows insertion order
- No way to break out of
forEach— usefor...ofwithbreakinstead if you need early exit forEach()always returnsundefined
Entries added during iteration
If new entries are added to the Map while forEach is running, those entries will be visited — the spec requires forEach to visit all entries that existed at the time it was called and any added after the start, in insertion order. Entries deleted before they are visited are skipped. This is different from Array.prototype.forEach, which snapshots the length at the start and ignores elements added beyond that length.
const m = new Map([['a', 1]]);
m.forEach((v, k) => {
if (k === 'a') m.set('b', 2); // 'b' will be visited
console.log(k); // 'a', then 'b'
});
Callback argument order
The callback signature is (value, key, map) — value comes first, then key. This is the reverse of what many people expect coming from plain objects, where you typically think of the key first. The order matches the Array.prototype.forEach callback (element, index, array) convention, where the “data” (value) comes before the “address” (key).
const m = new Map([['greeting', 'hello']]);
m.forEach((value, key) => {
console.log(key, '->', value); // greeting -> hello
});
Return value
Map.prototype.forEach() always returns undefined. Unlike Array.prototype.map(), which collects callback return values into a new array, forEach discards them. If you need to transform a Map’s values and collect results, use [...map.entries()].map(...) or iterate with for...of and build the result yourself.
forEach() vs for…of
Both iterate a Map in insertion order. Prefer for...of when you need break, continue, or return to control iteration flow. Use forEach when you prefer callback-style code or need to pass a thisArg.
const map = new Map([['a', 1], ['b', 2], ['c', 3]]);
// for...of — supports early exit
for (const [key, value] of map) {
if (value > 1) break;
console.log(key); // 'a'
}
// forEach — no early exit, but can pass thisArg
map.forEach(function(value, key) {
console.log(key, this.prefix + value);
}, { prefix: '> ' });
When forEach() fits better than a loop
forEach() reads well when you want to apply the same side effect to each entry and do not need early exit. Logging, collecting metrics, and building another structure are all good examples. It also gives you a callback signature that keeps the value and key together, which is convenient when the logic is naturally “do this for every entry.” If you need more control flow, a for...of loop is the better tool.
Remember the callback order
The callback receives (value, key, map), not (key, value). That order mirrors Array.prototype.forEach() more than object iteration utilities, so it can surprise people on first use. A quick mental check is to remember that forEach() hands you the thing stored in the Map first, then the key that points to it. Once that order feels natural, the method is straightforward.