JS Array Map vs forEach: Which Should You Use?

Compare JavaScript's map() and forEach() methods with clear examples, performance benchmarks, and decision criteria. Learn when each method is the right choice for iteration, transformation, and side effects.

JavaScriptbeginner
12 min read

Both map() and forEach() iterate over every element in an array and run a callback function on each one. The difference is what they do with the result. map() collects return values into a new array. forEach() ignores return values and returns undefined. This single distinction determines which method you should reach for in every situation.

The Core Difference

javascriptjavascript
const prices = [10, 20, 30, 40, 50];
 
// map(): returns a NEW array of transformed values
const withTax = prices.map(price => price * 1.08);
console.log(withTax); // [10.8, 21.6, 32.4, 43.2, 54]
 
// forEach(): returns undefined, used for side effects
prices.forEach(price => {
  console.log(`Price: $${price.toFixed(2)}`);
});
// Price: $10.00
// Price: $20.00
// ...

Side-by-Side Comparison

Featuremap()forEach()
ReturnsNew arrayundefined
PurposeTransform dataExecute side effects
ChainableYes (.map().filter().reduce())No (returns undefined)
Modifies originalNoNo (but callback can mutate objects)
Async/await supportDoes not await promisesDoes not await promises
Early exit (break)Not supportedNot supported
Skips empty slotsYesYes
Callback return valueCollected into output arrayIgnored

When to Use map()

Use map() when you need to transform each element and collect the results into a new array.

Transforming Data

javascriptjavascript
const users = [
  { firstName: "Alice", lastName: "Chen" },
  { firstName: "Bob", lastName: "Smith" },
  { firstName: "Carol", lastName: "Johnson" },
];
 
const fullNames = users.map(user => `${user.firstName} ${user.lastName}`);
console.log(fullNames); // ["Alice Chen", "Bob Smith", "Carol Johnson"]

Building UI Components

javascriptjavascript
const menuItems = [
  { label: "Home", href: "/" },
  { label: "About", href: "/about" },
  { label: "Contact", href: "/contact" },
];
 
const navLinks = menuItems.map(item =>
  `<a href="${item.href}">${item.label}</a>`
);
console.log(navLinks.join("\n"));

Chaining with Other Methods

The real power of map() is chaining. Because it returns an array, you can pipe results directly into filter(), reduce(), or another map():

javascriptjavascript
const orders = [
  { product: "Laptop", price: 999, quantity: 1 },
  { product: "Cable", price: 12.99, quantity: 5 },
  { product: "Mouse", price: 49, quantity: 2 },
  { product: "Monitor", price: 349, quantity: 1 },
];
 
const expensiveItemNames = orders
  .filter(order => order.price * order.quantity > 50)
  .map(order => order.product);
 
console.log(expensiveItemNames); // ["Laptop", "Mouse", "Monitor"]

When to Use forEach()

Use forEach() when you need to do something with each element without building a new array.

Logging and Debugging

javascriptjavascript
const steps = ["Initialize", "Connect", "Authenticate", "Fetch", "Render"];
 
steps.forEach((step, index) => {
  console.log(`[Step ${index + 1}] ${step}`);
});

Updating Existing Objects

javascriptjavascript
const inventory = [
  { sku: "A1", name: "Widget", stock: 100 },
  { sku: "B2", name: "Gadget", stock: 0 },
  { sku: "C3", name: "Gizmo", stock: 45 },
];
 
inventory.forEach(item => {
  item.status = item.stock > 0 ? "available" : "out-of-stock";
});
 
console.log(inventory[1].status); // "out-of-stock"

Populating External Data Structures

javascriptjavascript
const records = [
  { id: "u1", name: "Alice" },
  { id: "u2", name: "Bob" },
  { id: "u3", name: "Carol" },
];
 
const lookup = {};
records.forEach(record => {
  lookup[record.id] = record.name;
});
 
console.log(lookup); // { u1: "Alice", u2: "Bob", u3: "Carol" }

The Wasted Array Anti-Pattern

The most common mistake is using map() when you only need side effects. This creates a new array that is immediately discarded:

javascriptjavascript
const users = ["Alice", "Bob", "Carol"];
 
// Anti-pattern: map() return value is thrown away
users.map(user => {
  console.log(`Welcome, ${user}!`);
});
// Creates ["undefined", "undefined", "undefined"] and discards it
 
// Correct: forEach() when you only need side effects
users.forEach(user => {
  console.log(`Welcome, ${user}!`);
});
Do Not Ignore map() Output

If you use map() but never assign or use the returned array, switch to forEach(). Linters like ESLint flag this with the array-callback-return rule. Creating and discarding arrays wastes memory and confuses other developers reading your code.

Performance Comparison

For most applications, the speed difference between map() and forEach() is irrelevant. Both are O(n). The real difference is in memory allocation:

javascriptjavascript
const data = Array.from({ length: 100000 }, (_, i) => i);
 
// map(): allocates a new 100,000-element array
console.time("map");
const mapped = data.map(n => n * 2);
console.timeEnd("map"); // ~3-5ms
 
// forEach(): no new array allocated
console.time("forEach");
data.forEach(n => n * 2);
console.timeEnd("forEach"); // ~2-4ms
Metricmap()forEach()
Time complexityO(n)O(n)
MemoryAllocates new array (O(n))No allocation
Garbage collectionNew array needs GC if unusedNothing extra to collect
Practical differenceNegligible for arrays under 100KNegligible for arrays under 100K

Neither map() nor forEach() for These Cases

Both methods share limitations that make neither one the right choice in certain situations:

Need to Break Early

javascriptjavascript
const scores = [92, 85, 78, 95, 88, 70, 45, 82];
 
// Neither map nor forEach can break early
// Use for...of or find() instead
let firstFailing;
for (const score of scores) {
  if (score < 50) {
    firstFailing = score;
    break;
  }
}
 
// Or use find()
const firstFailing2 = scores.find(s => s < 50); // 45

Need Sequential Async Operations

javascriptjavascript
const files = ["config.json", "data.csv", "report.pdf"];
 
// Both map and forEach fire all callbacks synchronously
// Use for...of for sequential async
async function processFiles() {
  for (const file of files) {
    await uploadFile(file);
    console.log(`Uploaded: ${file}`);
  }
}

Need Both Transformation and Side Effects

javascriptjavascript
const items = [
  { name: "Widget", price: 25, discount: 0.1 },
  { name: "Gadget", price: 50, discount: 0.2 },
];
 
// Don't use map() with side effects mixed in
// Instead, separate concerns
const finalPrices = items.map(item => ({
  name: item.name,
  finalPrice: item.price * (1 - item.discount),
}));
 
// Then use forEach for side effects
finalPrices.forEach(item => {
  console.log(`${item.name}: $${item.finalPrice.toFixed(2)}`);
});

Decision Flowchart

Use this mental model when choosing between map(), forEach(), and other iteration methods:

CodeCode
Do you need a new array?
├── YES → Use map()
│         Can you chain it with filter/reduce? → Even better
└── NO
    ├── Do you need to stop early?
    │   ├── YES → Use for...of or for loop
    │   └── NO
    │       ├── Do you need async/await?
    │       │   ├── YES → Use for...of
    │       │   └── NO → Use forEach()
    │       └── Do you need a single value?
    │           └── YES → Use reduce()

Common Mistakes

Using map() for logging only:

javascriptjavascript
const items = ["a", "b", "c"];
 
// Bad: creates and discards an array
items.map(item => console.log(item));
 
// Good: forEach for pure side effects
items.forEach(item => console.log(item));

Chaining after forEach():

javascriptjavascript
const nums = [1, 2, 3];
 
// TypeError: Cannot read properties of undefined
// nums.forEach(n => n * 2).filter(n => n > 2);
 
// Fix: use map() when you need to chain
nums.map(n => n * 2).filter(n => n > 2); // [4, 6]

Confusing return behavior:

javascriptjavascript
const nums = [1, 2, 3, 4, 5];
 
// forEach return is ignored
const result = nums.forEach(n => {
  return n > 3; // This return does nothing useful
});
console.log(result); // undefined
 
// Use filter() when you need to select elements by condition
const big = nums.filter(n => n > 3);
console.log(big); // [4, 5]

Best Practices

  1. Default to map() for data transformations. Any time you need a new array derived from the original, map() is the right choice.
  2. Use forEach() only for side effects. Logging, mutations, sending events, populating external structures.
  3. Never ignore the map() return value. If you are not using the new array, switch to forEach().
  4. Separate transformation from side effects. Do not mix console.log() inside a map() callback. Map first, then forEach the results.
  5. Consider reduce() for accumulation. When you need a single value (sum, grouped object, count), reduce() is more expressive than building a result with forEach().
Rune AI

Rune AI

Key Insights

  • map() returns a new array: use it for transforming data and chaining with filter/reduce
  • forEach() returns undefined: use it for side effects like logging, mutations, and external writes
  • Never discard map() output: if you ignore the returned array, switch to forEach()
  • Neither supports break or async/await: use for...of or find() when you need early exit or sequential async
  • Performance is equal: choose based on intent (transformation vs side effects), not speed
RunePowered by Rune AI

Frequently Asked Questions

Can I use map() and forEach() on the same array?

Yes. They are independent operations. You can map() an array to create a new transformed array and then forEach() the original (or the mapped array) for side effects. Each call produces its own result without affecting the other.

Is map() always better than forEach()?

No. map() is better when you need transformed output. forEach() is better when you only need side effects. Using map() when you discard the result wastes memory and confuses intent. The right method depends on what you are doing with each element.

Do both methods skip empty slots in sparse arrays?

Yes. Both map() and forEach() skip empty slots (holes) created by `delete` or literal gaps like `[1, , 3]`. However, both visit elements that are explicitly set to `undefined`.

Can I make either method work with async/await?

Neither map() nor forEach() properly awaits promises inside callbacks. With map(), you get an array of promises you can pass to `Promise.all()`. forEach() fires callbacks without waiting. For sequential async iteration, use `for...of` with `await`.

Which one is faster?

For most real-world arrays (under 100K elements), the difference is undetectable. forEach() has a slight edge because it does not allocate a new array. map() allocates an output array of the same length. Choose based on purpose (transformation vs side effects), not performance.

Conclusion

The choice between map() and forEach() comes down to one question: do you need a new array? If yes, use map(). If you only need to execute side effects on each element, use forEach(). Never use map() when the return value is discarded, and never use forEach() when you need to chain or collect results. For cases where neither fits (early exit, async operations, single-value accumulation), reach for for...of, find(), or reduce().