JavaScriptIntermediate

JavaScript map, filter and reduce: Transform Arrays Easily

Master the three most useful array methods: map to transform, filter to select, and reduce to combine items, plus how to chain them with real examples.

All JavaScript lessons

What you will learn

Loops work, but they are long. To double every number in a list, you must create an empty array, write a loop, calculate, and push the result. JavaScript has three array methods that do these common jobs in one clear line: map (change every item), filter (keep only some items) and reduce (combine everything into one value). In this lesson you will learn how each one works, how to chain them into powerful pipelines, and how to avoid the classic mistakes.

A quick recap

You met these methods briefly in the callbacks lesson. Here is the big picture:

Method Question it answers Returns
map “What should each item become?” A new array, same length
filter “Should this item be kept?” A new array, same or shorter
reduce “How do I combine all the items?” One value (a number, text, object, array…)

All three:

  • take a callback function,
  • call it once for every item,
  • do not change the original array (they return something new).

map: transform every item

map creates a new array by running your function on each item and collecting the results.

const numbers = [1, 2, 3, 4];

const doubled = numbers.map((n) => n * 2);

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

The loop version, for comparison:

const numbers = [1, 2, 3, 4];
const doubledLoop = [];

for (const n of numbers) {
  doubledLoop.push(n * 2);
}

console.log(doubledLoop);   // [2, 4, 6, 8]

map says what you want, and the loop says how to do it.

The callback arguments

Your function receives three things (you usually need only the first one or two):

array.map((item, index, array) => { ... });
const names = ["Riya", "Karan", "Neha"];

const numbered = names.map((name, index) => `${index + 1}. ${name}`);
console.log(numbered);   // ["1. Riya", "2. Karan", "3. Neha"]

Pulling out one property

A very common use: take a list of objects and get one field from each.

const users = [
  { name: "Riya", age: 20 },
  { name: "Karan", age: 25 },
  { name: "Neha", age: 22 }
];

const names = users.map((user) => user.name);
console.log(names);   // ["Riya", "Karan", "Neha"]

Changing the shape of objects

const products = [
  { name: "Notebook", price: 100 },
  { name: "Pen", price: 20 }
];

const discounted = products.map((p) => ({
  name: p.name,
  price: p.price * 0.9
}));

console.log(discounted);
// [{ name: "Notebook", price: 90 }, { name: "Pen", price: 18 }]
console.log(products[0].price);   // 100 (original untouched)

Notice the round brackets around the object ({ ... }). Without them, JavaScript reads { } as a function body (as you saw in the arrow functions lesson).

map vs forEach

  • Use map when you want a new array of results.
  • Use forEach when you only want to do something (print, save) and need nothing back.
[1, 2, 3].forEach((n) => console.log(n));        // does something, returns undefined
const result = [1, 2, 3].map((n) => n * 2);     // gives back [2, 4, 6]

filter: keep only what you want

filter creates a new array with only the items for which your function returns a truthy value. A function that answers yes or no like this is called a predicate.

const numbers = [1, 2, 3, 4, 5, 6];

const evens = numbers.filter((n) => n % 2 === 0);

console.log(evens);     // [2, 4, 6]
console.log(numbers);   // [1, 2, 3, 4, 5, 6]  (unchanged)

The items themselves are not changed, only chosen. If nothing matches, you get an empty array [] (never undefined).

const marks = [72, 38, 90, 45, 28];

console.log(marks.filter((m) => m >= 40));    // [72, 90, 45]
console.log(marks.filter((m) => m > 100));    // []

Filtering objects

const products = [
  { name: "Notebook", price: 80, inStock: true },
  { name: "Headphones", price: 1500, inStock: true },
  { name: "Backpack", price: 900, inStock: false }
];

const available = products.filter((p) => p.inStock);
const cheap = products.filter((p) => p.price < 1000);
const cheapAndAvailable = products.filter((p) => p.inStock && p.price < 1000);

console.log(available.length);           // 2
console.log(cheap.length);               // 2
console.log(cheapAndAvailable.length);   // 1

Removing falsy values with filter(Boolean)

const data = ["Riya", "", null, "Karan", undefined, "Neha", 0];

console.log(data.filter(Boolean));   // ["Riya", "Karan", "Neha"]

Boolean is a function that turns any value into true or false, so it works as a ready-made predicate.

Removing one item (without changing the original)

const todos = [
  { id: 1, task: "Buy milk" },
  { id: 2, task: "Study JS" },
  { id: 3, task: "Walk" }
];

const idToRemove = 2;
const remaining = todos.filter((todo) => todo.id !== idToRemove);

console.log(remaining.map((t) => t.task));   // ["Buy milk", "Walk"]
console.log(todos.length);                   // 3 (original untouched)

This is the standard way to “delete” an item in modern apps.

Search as you type

const cities = ["Delhi", "Mumbai", "Dehradun", "Chennai", "Dubai"];
const search = "de";

const matches = cities.filter((city) =>
  city.toLowerCase().includes(search.toLowerCase())
);

console.log(matches);   // ["Delhi", "Dehradun"]

Unique values

const tags = ["js", "html", "js", "css", "html"];

const unique = tags.filter((tag, index) => tags.indexOf(tag) === index);
console.log(unique);   // ["js", "html", "css"]

An item is kept only if its first position equals the current position. (The shorter way is [...new Set(tags)].)

reduce: combine into one value

reduce is the most powerful (and the most confusing) of the three. It walks through the array, keeping a running result called the accumulator, and gives you back one final value.

const result = array.reduce((accumulator, item) => newAccumulator, startValue);

Step by step: adding numbers

const numbers = [10, 20, 30];

const sum = numbers.reduce((total, n) => total + n, 0);

console.log(sum);   // 60

Read it as: “start with 0. For each number, add it to the total.” Here is what happens inside:

Round total (before) n Returned (becomes next total)
1 0 (the start value) 10 10
2 10 20 30
3 30 30 60

After the last round, reduce returns 60. Whatever your function returns becomes the accumulator for the next round.

The same job with a loop

const numbers = [10, 20, 30];
let total = 0;

for (const n of numbers) {
  total = total + n;
}

console.log(total);   // 60

The accumulator is total, and the start value is 0. Once you see that, reduce is just a loop with the variable tucked inside.

More examples with numbers

const prices = [250, 99, 1499, 75];

// Product of all numbers
console.log([2, 3, 4].reduce((acc, n) => acc * n, 1));   // 24

// Largest number
const highest = prices.reduce((max, p) => (p > max ? p : max), prices[0]);
console.log(highest);   // 1499

// Average
const average = prices.reduce((sum, p) => sum + p, 0) / prices.length;
console.log(average);   // 480.75

The start value can be anything

The accumulator does not have to be a number. It can be a string, an array or an object. That is where reduce really shines.

Count how often each item appears (object):

const fruits = ["apple", "banana", "apple", "mango", "apple", "banana"];

const counts = fruits.reduce((result, fruit) => {
  result[fruit] = (result[fruit] || 0) + 1;
  return result;
}, {});

console.log(counts);   // { apple: 3, banana: 2, mango: 1 }

Group items by a property (object of arrays):

const products = [
  { name: "Notebook", category: "stationery" },
  { name: "Headphones", category: "electronics" },
  { name: "Pen", category: "stationery" },
  { name: "Mouse", category: "electronics" }
];

const grouped = products.reduce((groups, product) => {
  const key = product.category;

  if (!groups[key]) {
    groups[key] = [];
  }

  groups[key].push(product.name);
  return groups;
}, {});

console.log(grouped);
// { stationery: ["Notebook", "Pen"], electronics: ["Headphones", "Mouse"] }

Build a lookup by id:

const users = [
  { id: 101, name: "Riya" },
  { id: 102, name: "Karan" }
];

const usersById = users.reduce((lookup, user) => {
  lookup[user.id] = user;
  return lookup;
}, {});

console.log(usersById[102].name);   // "Karan"

Flatten an array of arrays (array accumulator):

const nested = [[1, 2], [3], [4, 5]];

const flat = nested.reduce((all, group) => all.concat(group), []);
console.log(flat);   // [1, 2, 3, 4, 5]

Always give reduce a start value

Without a start value, reduce uses the first item as the start and begins from the second. That works for sums, but it breaks easily:

console.log([].reduce((a, b) => a + b));      // TypeError: Reduce of empty array with no initial value
console.log([].reduce((a, b) => a + b, 0));   // 0  (safe)

Also, if your accumulator is an object or array, you must provide the start value ({} or []), or the first item would be used by mistake.

Do not forget to return

The most common reduce bug is forgetting to return the accumulator:

const wrong = [1, 2, 3].reduce((total, n) => {
  total + n;   // no return! total becomes undefined in the next round
}, 0);

console.log(wrong);   // undefined

reduceRight

reduceRight works the same, but starts from the last item and goes backwards. You will rarely need it.

Chaining: combining the three

Because map and filter return arrays, you can call another method immediately on the result. This is called method chaining.

const products = [
  { name: "Notebook", price: 80, inStock: true },
  { name: "Headphones", price: 1500, inStock: true },
  { name: "Backpack", price: 900, inStock: false },
  { name: "Pen", price: 20, inStock: true }
];

const total = products
  .filter((p) => p.inStock)        // keep only items in stock
  .map((p) => p.price)             // take just the prices
  .reduce((sum, price) => sum + price, 0);   // add them up

console.log(total);   // 1600

Read it like a recipe, from top to bottom: “Take the products, keep those in stock, get their prices, add them up.”

How the data flows

products          → filter →         → map →        → reduce →
[4 objects]         [3 objects]        [80, 1500, 20]   1600

Each step receives the output of the step before.

Tips for good chains

  • Put filter first, so the later steps have fewer items to work on.
  • One step per line, with the dot at the start of each line, so it reads like a list of instructions.
  • Debug by splitting the chain into variables or by adding a console.log in the middle:
const products = [
  { name: "Notebook", price: 80, inStock: true },
  { name: "Backpack", price: 900, inStock: false }
];

const result = products
  .filter((p) => p.inStock)
  .map((p) => {
    console.log("Mapping", p.name);   // peek inside the chain
    return p.price;
  });

console.log(result);   // [80]
  • You can add sort or slice in a chain too. Copy the array first if you call sort, because it changes the array: [...products].sort(...).

When to use a loop instead

map, filter and reduce always walk through every item. Prefer a normal for...of loop when you need to:

  • stop early with break (for example, find the first match),
  • skip items with continue,
  • or run asynchronous code step by step with await (a later lesson).

For stopping at the first match, the next lesson introduces find and some.

Real-life use cases

1. Show a list on the page

const items = ["HTML", "CSS", "JavaScript"];

const html = items.map((item) => `<li>${item}</li>`).join("");
console.log(html);   // "<li>HTML</li><li>CSS</li><li>JavaScript</li>"

map builds a piece of HTML for each item, and join("") glues them into one string. You can then put that string into the page using innerHTML (the DOM lessons explain it). This is how many small apps draw lists.

2. Shopping cart total with quantities

const cart = [
  { name: "Notebook", price: 80, qty: 3 },
  { name: "Pen", price: 20, qty: 5 },
  { name: "Bag", price: 900, qty: 1 }
];

const total = cart.reduce((sum, item) => sum + item.price * item.qty, 0);

console.log(`Total: ₹${total}`);   // "Total: ₹1240"

3. Total after discount and tax (a full chain)

const prices = [100, 250, 400, 50];

const finalTotal = prices
  .filter((price) => price >= 100)         // only items of ₹100 or more
  .map((price) => price * 0.9)             // 10% discount
  .map((price) => price * 1.18)            // 18% GST
  .reduce((sum, price) => sum + price, 0);

console.log(finalTotal.toFixed(2));   // "796.50"

4. Average rating

const ratings = [5, 4, 3, 5, 4, 4];

const average = ratings.reduce((sum, r) => sum + r, 0) / ratings.length;

console.log(average.toFixed(1));   // "4.2"

5. Count orders by status

const orders = [
  { id: 1, status: "delivered" },
  { id: 2, status: "pending" },
  { id: 3, status: "delivered" },
  { id: 4, status: "cancelled" },
  { id: 5, status: "delivered" }
];

const byStatus = orders.reduce((counts, order) => {
  counts[order.status] = (counts[order.status] || 0) + 1;
  return counts;
}, {});

console.log(byStatus);   // { delivered: 3, pending: 1, cancelled: 1 }

6. Names of the most expensive products

const products = [
  { name: "Notebook", price: 80 },
  { name: "Headphones", price: 1500 },
  { name: "Backpack", price: 900 },
  { name: "Pen", price: 20 }
];

const premium = products
  .filter((p) => p.price >= 500)
  .map((p) => p.name.toUpperCase());

console.log(premium);   // ["HEADPHONES", "BACKPACK"]

7. Format prices for display

const prices = [1999, 49.5, 120000];

const formatted = prices.map((price) =>
  price.toLocaleString("en-IN", { style: "currency", currency: "INR" })
);

console.log(formatted);   // ["₹1,999.00", "₹49.50", "₹1,20,000.00"]

8. Update one item in a list without changing the original

const todos = [
  { id: 1, task: "Buy milk", done: false },
  { id: 2, task: "Study JS", done: false }
];

const updated = todos.map((todo) =>
  todo.id === 2 ? { id: todo.id, task: todo.task, done: true } : todo
);

console.log(updated[1].done);   // true
console.log(todos[1].done);     // false (original untouched)

map returns each item as it is, except the one we want to change. Apps built with React use this exact pattern to update state.

9. Convert text to numbers safely

const input = ["10", "20", "abc", "30"];

const numbers = input.map(Number).filter((n) => !Number.isNaN(n));

console.log(numbers);   // [10, 20, 30]

Common mistakes

  • Forgetting return in the callback. With braces { }, you must write return. Otherwise map gives [undefined, ...], and reduce breaks.
  • Using map when you do not need a new array. Use forEach for simple actions.
  • Using filter and expecting the items to change. It only selects items.
  • Forgetting the start value in reduce. An empty array throws a TypeError, and objects as accumulators break.
  • Forgetting to return the accumulator from the reduce callback.
  • Passing a function with the wrong parameters. ["1","2","3"].map(parseInt) gives [1, NaN, NaN]. Use .map(Number).
  • Modifying the original objects inside map. map((p) => { p.price = 0; return p; }) changes the original objects. Return a new object instead.
  • Putting filter last in a long chain. Filter first to avoid unnecessary work.
  • Writing a reduce that is hard to read. If a chain of map and filter is clearer, use that. Use reduce for sums, counting, grouping and building objects.
  • Forgetting round brackets when returning an object from an arrow function: (p) => ({ ... }).
  • Calling these methods on something that is not an array. undefined.map(...) crashes. Make sure the value is an array.

Practice

  1. Use map to turn [1, 2, 3, 4, 5] into their squares.
  2. Use map to turn ["riya", "karan", "neha"] into capitalised names ("Riya", …).
  3. Use filter to keep the numbers greater than 10 from [5, 12, 8, 130, 44].
  4. Use filter to keep only words with 5 or more letters from ["sun", "planet", "moon", "galaxy", "star"].
  5. Use reduce to calculate the sum of [12, 45, 7, 89, 23], and then the average.
  6. Given a list of products with name, price and inStock, print the names of the in-stock products that cost less than ₹500, using a chain.
  7. Use reduce to count the vowels in "javascript" (hint: [..."javascript"] makes an array of letters).
  8. Use reduce to turn [["a", 1], ["b", 2]] into the object { a: 1, b: 2 }.
  9. Write a chain that takes ["10", "x", "25", "", "40"], converts to numbers, removes the invalid ones, and returns their total.
  10. Challenge: given an array of orders ({ customer, amount }), use reduce to produce an object with each customer’s total amount.

Recap

  • map transforms every item and returns a new array of the same length.
  • filter keeps the items for which the callback returns a truthy value, and returns a new array (possibly empty).
  • reduce combines all items into one value. Give it a start value, and always return the accumulator.
  • None of the three changes the original array. They are the building blocks of clean, modern JavaScript.
  • Use map to change, filter to choose, reduce to combine, and forEach just to do something.
  • Chain them for pipelines. Put filter first, one step per line.
  • Use a normal loop when you need break, continue or await.
  • Next you will learn how to search and test arrays with find, findIndex, some, every and includes.