A list of users to display, a list of prices to convert, a list of identifiers to send: in all three cases you start from an array and want the same number of items, in a different shape.
That is exactly the job of map, and the only one it agrees to do.
Definition
map is an Array method that calls a function on each item and hands back a new array made of the returned values. The starting array stays intact, and the resulting array always has the same length.
const prices = [10, 25, 4];
const doubled = prices.map((p) => p * 2);
console.log(doubled); // [ 20, 50, 8 ]
console.log(prices); // [ 10, 25, 4 ], intact
const users = [{ name: "Ada" }, { name: "Alan" }];
console.log(users.map((u) => u.name)); // [ 'Ada', 'Alan' ]That preserved length is the most useful property of map: the output keeps the positions of the input, which lets the two arrays be matched up afterwards. Array.filter(), by contrast, breaks that correspondence.
The full signature
The function receives three arguments, of which only the first is used most of the time.
| Argument | What it holds |
|---|---|
item | The value of the current pass |
index | Its position, starting at zero |
array | The array being walked, in full |
const letters = ["a", "b", "c"];
console.log(letters.map((letter, i) => `${i + 1}. ${letter}`));
// [ '1. a', '2. b', '3. c' ]The two classic traps
The first is forgetting the return. A brace opens a function body, which hands back undefined as long as nothing is returned explicitly.
const wrong = [1, 2].map((n) => { n * 2; });
console.log(wrong); // [ undefined, undefined ]The second is handing over a ready-made function without looking at its arguments.
console.log(["10", "10", "10"].map(parseInt)); // [ 10, NaN, 2 ]
console.log(["10", "10", "10"].map(Number)); // [ 10, 10, 10 ]map passes the index as a second argument, and parseInt reads that second argument as a numeric base. The second call therefore converts in base 1, which does not exist, and the third in base 2.
Frequently asked questions
map or Array.forEach()?
The question is settled on the return value. map builds an array, so something has to be done with it. If the goal is to act, writing to a log or sending a message, forEach states the intent without producing an array nobody will read.
Should filtering come before or after?
Before, whenever possible. Filtering first cuts the number of items to transform, whereas the reverse order transforms values headed for the bin. On a short array the difference goes unseen, on ten thousand rows it is measurable.
Why is map everywhere in React?
Because displaying a list amounts to turning an array of data into an array of elements, which map does in a single expression. Each produced element also demands a stable key property, a point detailed in the React course.