Array.map()
Array.prototype.map() is the single most useful array method for transforming data. It takes a callback, runs it once for every element, and collects the return values into a brand-new array of exactly the same length. The original array is never touched, which makes map a cornerstone of predictable, functional-style JavaScript — perfect for reshaping API responses, rendering lists in UI frameworks, and pipelines that chain one transformation into the next.
How map works
You call map on an array and pass it a function. That function receives each element, and whatever it returns becomes the corresponding element in the output array. Because the result is a new array, you typically assign it to a new variable.
const numbers = [1, 2, 3, 4];
const doubled = numbers.map((n) => n * 2);
console.log(doubled);
console.log(numbers); // unchanged
Output:
[ 2, 4, 6, 8 ]
[ 1, 2, 3, 4 ]
The output always has the same length as the input — map is a one-to-one transformation. If you need to remove elements, reach for filter; if you need to collapse to a single value, reach for reduce.
The callback signature
The callback receives up to three arguments. Most of the time you only use the first, but the index and the source array are there when you need them.
| Parameter | Description |
|---|---|
value | The current element being processed. |
index | The zero-based position of the current element. |
array | The whole array map was called on. |
const fruits = ["apple", "banana", "cherry"];
const labeled = fruits.map((fruit, index) => `${index + 1}. ${fruit}`);
console.log(labeled);
Output:
[ '1. apple', '2. banana', '3. cherry' ]
A common real-world use is reshaping objects — for example, pulling one field out of a list of records:
const users = [
{ id: 1, name: "Ada" },
{ id: 2, name: "Linus" },
{ id: 3, name: "Grace" },
];
const names = users.map((user) => user.name);
console.log(names);
Output:
[ 'Ada', 'Linus', 'Grace' ]
Gotcha: When returning an object literal from an arrow function, wrap it in parentheses:
map((u) => ({ id: u.id })). Without them, JavaScript reads{as the start of a function body, not an object, and you’ll getundefinedfor every element.
Chaining transformations
Because map returns an array, you can chain it with other array methods to build readable pipelines. Each step describes one clear transformation.
const prices = [10, 25, 40, 5];
const total = prices
.map((price) => price * 1.2) // add 20% tax
.filter((price) => price > 10) // keep the bigger ones
.reduce((sum, price) => sum + price, 0);
console.log(total.toFixed(2));
Output:
72.00
This reads top-to-bottom like a sentence: take the prices, add tax, drop the cheap ones, sum what’s left.
map vs forEach
Both map and forEach run a callback for every element, so beginners often confuse them. The difference is the return value and your intent.
| Aspect | map | forEach |
|---|---|---|
| Returns | A new array | undefined |
| Purpose | Transform data | Run side effects |
| Chainable | Yes | No |
| Skips empty slots | Yes | Yes |
Use map when you want a new array back. Use forEach when you only care about doing something with each element (logging, mutating external state) and don’t need a result.
// Wrong: map used purely for a side effect
[1, 2, 3].map((n) => console.log(n)); // creates a throwaway [undefined, undefined, undefined]
// Right: forEach for side effects
[1, 2, 3].forEach((n) => console.log(n));
Tip: If you find yourself ignoring the array
mapreturns, that’s a strong signal you wantedforEachinstead. Linters like ESLint flag this with thearray-callback-returnrule.
Sparse arrays and async callbacks
map skips empty slots in sparse arrays but preserves them in the result, so [1, , 3].map((n) => n * 2) yields [2, <empty>, 6]. More importantly, map is not async-aware: an async callback makes map return an array of Promises, not resolved values. Wrap it in Promise.all to await them all.
const urls = ["/a", "/b"];
const responses = await Promise.all(
urls.map(async (url) => {
const res = await fetch(url);
return res.json();
}),
);
console.log(responses.length);
Output:
2
Best Practices
- Always use the returned array — never call
mapfor side effects; useforEachor afor...ofloop for those. - Keep callbacks pure: return a value based only on the input, without mutating the original elements or outside state.
- Prefer
mapover manualforloops when you’re building a transformed array of the same length — it’s clearer and self-documenting. - Wrap object-literal returns in parentheses to avoid the silent
undefinedtrap. - Reach for
Promise.all(arr.map(async ...))when each transformation is asynchronous. - Chain
mapwithfilterandreduceto express multi-step logic as a readable pipeline.