What Is the Difference Between Map, Filter, and Reduce in JavaScript?

map transforms every element into a new array of equal length, filter creates a new array containing only elements that pass a test, and reduce accumulates all elements into a single value—while all three preserve the original array's immutability.

The leonardomso/33-js-concepts repository demonstrates these core array methods through rigorous test cases in tests/functional-programming/map-reduce-filter/map-reduce-filter.test.js. Understanding the distinct return values and iteration patterns of each method enables you to select the right tool for data transformation, selection, or aggregation tasks without mutating source data.

How Each Method Transforms Data

All three methods are higher-order functions that accept a callback and execute it once per array element. According to the immutability assertions in lines 13-19 of map-reduce-filter.test.js, the original array remains [1,2,3] after any operation completes.

Array.prototype.map(): One-to-One Transformation

The map() method returns a new array where each element is the result of invoking your callback on the corresponding original element. The output length always matches the input length, making it ideal for property extraction or calculations.

Use map when converting an array of objects into values, as demonstrated in lines 5-11 of the test suite:

const numbers = [1, 2, 3, 4];
const doubled = numbers.map(n => n * 2);
// Result: [2, 4, 6, 8]

Array.prototype.filter(): Subset Selection

The filter() method returns a new array containing only elements for which your callback returns a truthy value. This effectively selects a subset of data based on a predicate condition.

Use filter when you need to remove unwanted items, such as extracting even numbers shown in lines 25-27:

const numbers = [1, 2, 3, 4, 5, 6];
const evens = numbers.filter(n => n % 2 === 0);
// Result: [2, 4, 6]

Array.prototype.reduce(): Aggregation and Folding

The reduce() method returns a single value—which can be a number, object, or another array—by applying your callback to each element in sequence. It maintains an accumulator that carries the running result between iterations.

Use reduce for aggregation tasks like summing values or building lookup maps. The callback receives (accumulator, element, index, array) and must return the new accumulator value. Lines 68-73 demonstrate basic summation:

const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, n) => acc + n, 0);
// Result: 15

If you omit the initialValue parameter, reduce uses the first array element as the initial accumulator and skips the first iteration.

When to Use Each Method

Choosing the correct method clarifies your code's intent and prevents over-engineering. The test suite in leonardomso/33-js-concepts identifies specific decision points:

  • Transform data shape: Use map to extract properties or calculate derived values (e.g., user => user.name).
  • Select valid entries: Use filter to keep items matching criteria (e.g., active users, positive numbers).
  • Compute totals or build objects: Use reduce for aggregation (sum, average) or complex grouping. Lines 20-27 demonstrate a group-by-property implementation where reduce builds a map of arrays ({key: [items]}).

Chaining for Complex Pipelines

For multi-step operations, chain the methods rather than creating a monolithic reduce. Lines 26-31 demonstrate a real-world pipeline that filters in-stock products, maps to prices, then reduces to a total:

const products = [
  { name: 'Laptop', price: 1000, inStock: true },
  { name: 'Phone',  price: 500,  inStock: false },
  { name: 'Tablet', price: 300,  inStock: true }
];

const totalInStock = products
  .filter(p => p.inStock)
  .map(p => p.price)
  .reduce((sum, price) => sum + price, 0);
// Result: 1300

Lines 50-68 specifically caution against cramming filter-and-map logic into a single reduce block, as this reduces readability while providing no performance benefit in modern JavaScript engines.

Advanced Reduce Patterns

Because reduce is the most flexible method, you can theoretically implement the others with it—though you shouldn't in production code. Lines 46-55 demonstrate implementing map using reduce to illustrate the underlying mechanics:

const doubled = numbers.reduce((acc, n) => {
  acc.push(n * 2);
  return acc;
}, []);

Summary

  • map returns a new array of equal length, transforming each element individually—ideal for property extraction or calculations.
  • filter returns a new array of variable length, keeping only elements where the callback returns truthy—use for subset selection.
  • reduce returns any single value by accumulating results across iterations—essential for aggregation, grouping, or folding operations.
  • Immutability: All three methods leave the source array untouched, as verified by the test assertions in lines 13-19 of map-reduce-filter.test.js.
  • Readability: Prefer chaining filter → map → reduce over complex single-reduce solutions that mix concerns.

Frequently Asked Questions

Can I use reduce instead of map or filter?

Technically yes, but you shouldn't. Lines 46-55 of map-reduce-filter.test.js prove that reduce can implement map by pushing to an accumulator array. However, using map or filter explicitly communicates your intent and makes the code self-documenting. Reserve reduce for true aggregation tasks.

Do map, filter, and reduce mutate the original array?

No. All three methods return new values and leave the source array unchanged. The repository's immutability tests (lines 13-19) assert that the original array stays [1,2,3] after any operation, confirming these are pure functional utilities.

Is it better to chain methods or use a single reduce?

For readability, chain methods. Lines 50-68 demonstrate that while a single reduce can perform filtering and mapping simultaneously, separating concerns into filter().map().reduce() creates clearer, more maintainable code. Each method traverses the array once, and modern JavaScript engines optimize chained operations effectively.

What happens if I call reduce on an empty array without an initial value?

JavaScript throws a TypeError. The reduce method requires an initialValue when the array is empty because it cannot determine an initial accumulator. Always provide an initialValue argument (such as 0 for sums or [] for arrays) when the input array might be empty to avoid runtime errors.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →