Episode 1 — Fundamentals / 1.22 — Array Methods

1.22.a — map()

In one sentence: map() calls a function on every element and returns a brand-new array of the same length containing each transformed value.

Navigation: ← 1.22 Overview · 1.22.b — filter() →


1. What map() does

map() iterates through every element, passes it to your callback, and collects the return values into a fresh array. The original array is never touched.

const nums = [1, 2, 3, 4];
const doubled = nums.map(n => n * 2);

console.log(doubled); // [2, 4, 6, 8]
console.log(nums);    // [1, 2, 3, 4]  ← unchanged

Think of it as a conveyor belt: each item goes in, gets transformed, and comes out the other end.


2. Syntax

const newArray = arr.map(callback(element, index, array));
ParameterDescription
elementThe current element being processed
index(optional) The index of the current element
array(optional) The original array map was called on
ReturnA new array with each element being the result of the callback

The callback must return a value. Whatever you return becomes the element at that position in the new array.

const words = ["hello", "world"];

// Using all three parameters
const tagged = words.map((word, index, arr) => {
  return `${index + 1}/${arr.length}: ${word}`;
});

console.log(tagged);
// ["1/2: hello", "2/2: world"]

3. Does NOT mutate the original

This is a core guarantee:

const prices = [10, 20, 30];
const withTax = prices.map(p => p * 1.18);

console.log(prices);  // [10, 20, 30]    ← same
console.log(withTax); // [11.8, 23.6, 35.4] ← new array

This makes map safe for functional programming and React state updates where immutability matters.


4. Return value is always a new array of the same length

No matter what your callback does, map always returns an array with exactly as many elements as the original.

const nums = [1, 2, 3];
const result = nums.map(n => {
  if (n > 2) return n * 10;
  // oops, no explicit return for n <= 2
});

console.log(result); // [undefined, undefined, 30]
//                       ^^^^^^^^^ still 3 elements!

If you want fewer elements, use filter. If you want a single value, use reduce.


5. Common use: transforming data shapes

One of the most frequent real-world uses is reshaping objects from one form to another.

// API response
const users = [
  { id: 1, first_name: "Alice", last_name: "Smith", email: "alice@example.com" },
  { id: 2, first_name: "Bob", last_name: "Jones", email: "bob@example.com" },
  { id: 3, first_name: "Carol", last_name: "White", email: "carol@example.com" },
];

// Transform to UI-friendly shape
const displayUsers = users.map(user => ({
  id: user.id,
  fullName: `${user.first_name} ${user.last_name}`,
  avatar: user.first_name[0] + user.last_name[0],
}));

console.log(displayUsers);
// [
//   { id: 1, fullName: "Alice Smith", avatar: "AS" },
//   { id: 2, fullName: "Bob Jones",   avatar: "BJ" },
//   { id: 3, fullName: "Carol White", avatar: "CW" },
// ]

6. Mapping to JSX in React (preview)

If you continue to React, map becomes your primary tool for rendering lists:

function UserList({ users }) {
  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.fullName}</li>
      ))}
    </ul>
  );
}

Every React list render is basically a map call. The key prop tells React which element changed.


7. map vs forEach — key difference

Featuremap()forEach()
ReturnsNew array of transformed valuesundefined
Use forTransforming dataSide effects (logging, DOM updates)
ChainableYes (arr.map(...).filter(...))No (returns undefined)
Original mutatedNoNo

Rule of thumb: If you need the result, use map. If you just need to do something for each element, use forEach.

// WRONG: using forEach when you need the result
const result = [];
[1, 2, 3].forEach(n => result.push(n * 2)); // works but messy

// RIGHT: use map
const result2 = [1, 2, 3].map(n => n * 2);  // clean and declarative

8. Chaining with other methods

Because map returns an array, you can chain more array methods:

const products = [
  { name: "Laptop", price: 999, inStock: true },
  { name: "Phone", price: 699, inStock: false },
  { name: "Tablet", price: 499, inStock: true },
  { name: "Watch", price: 299, inStock: true },
];

// Chain: filter in-stock → map to formatted strings → sort alphabetically
const available = products
  .filter(p => p.inStock)
  .map(p => `${p.name}: $${p.price}`)
  .sort();

console.log(available);
// ["Laptop: $999", "Tablet: $499", "Watch: $299"]

Read the chain top-to-bottom: filter first (reduces the set), then map (transforms), then sort.


9. Common mistake: forgetting to return

With arrow functions using curly braces {}, you need an explicit return:

// BUG: no return statement
const names = ["alice", "bob"].map(name => {
  name.toUpperCase(); // computed but not returned!
});
console.log(names); // [undefined, undefined]

// FIX 1: add return
const names2 = ["alice", "bob"].map(name => {
  return name.toUpperCase();
});

// FIX 2: use implicit return (no braces)
const names3 = ["alice", "bob"].map(name => name.toUpperCase());

Another common mistake with objects — you need parentheses for implicit return:

// BUG: braces are interpreted as function body, not object literal
const pairs = [1, 2, 3].map(n => { value: n });
console.log(pairs); // [undefined, undefined, undefined]

// FIX: wrap object literal in parentheses
const pairs2 = [1, 2, 3].map(n => ({ value: n }));
console.log(pairs2); // [{ value: 1 }, { value: 2 }, { value: 3 }]

10. Real-world examples

Double all numbers

const nums = [4, 9, 16, 25];
const doubled = nums.map(n => n * 2);
// [8, 18, 32, 50]

Extract names from objects

const students = [
  { name: "Alice", grade: "A" },
  { name: "Bob", grade: "B" },
  { name: "Carol", grade: "A" },
];

const names = students.map(s => s.name);
// ["Alice", "Bob", "Carol"]

Format prices

const rawPrices = [19.99, 5.5, 100, 0.99];

const formatted = rawPrices.map(price =>
  `$${price.toFixed(2)}`
);
// ["$19.99", "$5.50", "$100.00", "$0.99"]

Convert Celsius to Fahrenheit

const celsius = [0, 20, 37, 100];
const fahrenheit = celsius.map(c => (c * 9/5) + 32);
// [32, 68, 98.6, 212]

Add computed property to objects

const orders = [
  { item: "Shirt", qty: 2, unitPrice: 25 },
  { item: "Pants", qty: 1, unitPrice: 50 },
  { item: "Socks", qty: 5, unitPrice: 5 },
];

const withTotals = orders.map(order => ({
  ...order,
  total: order.qty * order.unitPrice,
}));

// [
//   { item: "Shirt", qty: 2, unitPrice: 25, total: 50 },
//   { item: "Pants", qty: 1, unitPrice: 50, total: 50 },
//   { item: "Socks", qty: 5, unitPrice: 5,  total: 25 },
// ]

11. Using the index parameter

const letters = ["a", "b", "c"];

const numbered = letters.map((letter, index) => `${index + 1}. ${letter}`);
// ["1. a", "2. b", "3. c"]

Be careful: do not accidentally use the index as a key for non-stable lists in React (covered in React modules).


12. map with existing functions

You can pass a named function directly:

function square(n) {
  return n * n;
}

const nums = [1, 2, 3, 4, 5];
const squares = nums.map(square);
// [1, 4, 9, 16, 25]

Gotcha with parseInt:

// Surprising behavior!
["1", "2", "3"].map(parseInt);
// [1, NaN, NaN]  ← because parseInt receives (element, index) as (string, radix)

// Fix: wrap in arrow function
["1", "2", "3"].map(s => parseInt(s, 10));
// [1, 2, 3]

Key takeaways

  1. map() transforms every element and returns a new array of the same length.
  2. It never mutates the original array.
  3. Always return a value from the callback — otherwise you get undefined in the new array.
  4. Use map when you need the result; use forEach when you only need side effects.
  5. Wrap object literals in parentheses for implicit arrow-function returns: n => ({ key: n }).
  6. map is the workhorse of React list rendering and data transformation pipelines.

Explain-It Challenge

Explain without notes:

  1. What does map() return if the callback never has a return statement?
  2. Why is [10, 20, 30].map(n => { doubled: n * 2 }) broken, and how do you fix it?
  3. When would you pick forEach over map?

Navigation: ← 1.22 Overview · 1.22.b — filter() →