jsguides

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

ParameterTypeDefaultDescription
callbackFnfunctionFunction called for each key-value pair
valueanyThe current value
keyanyThe current key
mapMapThe Map being iterated
thisArganyundefinedValue 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 — use for...of with break instead if you need early exit
  • forEach() always returns undefined

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.

See Also