What you'll be able to do

By the end of this page you should be able to walk an array with forEach when you only need a side effect, build a new array with map from each item’s return value, and avoid the habit of using map just to console.log or mutate in place.

Previous post: arrays — basics and mutation.

Who this is for

  • People who write for loops for every list walk and want the array methods that match the job
  • Beginners who use map for printing and ignore the new array it returns
  • Anyone confused why forEach “returns nothing useful”

You can skip this if you already pick map for transforms and forEach (or for...of) for side effects. Come back when a teammate asks why your map result is full of undefined.

forEach: do something with each item

forEach calls your function once per item. It does not build a new array for you. The useful work is usually a side effect: log, update the DOM, push somewhere else.

const tags = ["javascript", "tutorials"];
tags.forEach(function (tag) {
  console.log(tag);
});

Scenario A — print every tag. forEach is fine; you do not need a new list.

Scenario B — update paragraphs in the page. Same idea: each call changes the outside world. That is impure on purpose.

forEach’s own return value is undefined. Do not assign const result = tags.forEach(...) and expect items.

map: build a new array from return values

map also visits each item, but it collects whatever you return into a brand-new array. The original array stays as it was (unless you mutate items inside — don’t, for learning).

const prices = [10, 20, 5];
const withTax = prices.map(function (price) {
  return price * 1.1;
});
console.log(withTax); // [11, 22, 5.5]
console.log(prices); // [10, 20, 5]

Scenario A — double every score for display. map returns the new numbers; keep the source list clean.

Scenario B — turn ids into label strings. ids.map((id) => "user-" + id) gives a parallel list of strings.

If you forget return, every slot becomes undefined — a classic beginner bug.

The wrong habit: map only for side effects

// Awkward — map builds an unused array of undefined
["a", "b"].map(function (item) {
  console.log(item);
});

Scenario A — “I heard map is modern.” For logging or DOM writes, prefer forEach or for...of.

Scenario B — you need both a new list and a log. Prefer map for the list; log separately, or log inside map only while debugging — the return value is still the point of map.

Rule of thumb: transform → map. Do a thing → forEach / loop.

Callback arguments you will see

Both methods pass (item, index, array). Beginners usually need item; index helps when the position matters.

["a", "b"].map(function (item, index) {
  return index + ":" + item;
});
// ["0:a", "1:b"]

Scenario A — numbered labels. Use index.

Scenario B — ignore index. That is fine; omit it from the parameter list.

Arrow form works the same once you are comfortable: prices.map((p) => p * 2).

forEach cannot “return early” from the outer function the way people hope

return inside the callback only exits that one callback call, not a surrounding function. break does not work in forEach.

Scenario A — stop when you find a match. Use find, some, or a for...of loop with break (coming soon / loops you already know).

Scenario B — you only wanted side effects on every item. forEach is still fine.

A tiny practice file

Save as map-foreach.html.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>forEach and map</title>
  </head>
  <body>
    <button id="log">forEach log</button>
    <button id="double">map double</button>
    <pre id="out"></pre>
    <script>
      const nums = [1, 2, 3];
      const out = document.getElementById("out");

      document.getElementById("log").addEventListener("click", function () {
        const lines = [];
        nums.forEach(function (n) {
          lines.push("saw " + n);
        });
        out.textContent = lines.join("\n") + "\nnums still " + JSON.stringify(nums);
      });

      document.getElementById("double").addEventListener("click", function () {
        const doubled = nums.map(function (n) {
          return n * 2;
        });
        out.textContent =
          "doubled " + JSON.stringify(doubled) + "\nnums still " + JSON.stringify(nums);
      });
    </script>
  </body>
</html>

One button only logs; the other builds a new list and leaves nums alone.

Mistakes I see a lot

1. map without return. You get [undefined, undefined, ...].

2. Using map for console.log only. Prefer forEach.

3. Mutating the original inside map. Confusing; return a new value instead.

4. Expecting forEach to return a transformed array. It does not.

5. Trying to break out of forEach. Switch tools.

6. Chaining map then ignoring the result. Dead work — assign or return it.

What to try before the next post

  1. forEach log every item in ["x","y"].
  2. map strings to uppercase with return.
  3. Deliberately omit return in map and inspect the result.
  4. Build the tiny HTML file.

Next in this series: filter, find, and findIndex — keep some items, or pick the first match.

Try this next outside the series

Arrays get easier when you see them render UI lists and move through real transforms.