Chapter 6.3☕ 20 min read

map, filter, reduce & Anti-patterns

map naye array banata hai, filter chhanta lagata hai, aur reduce ek value mein sab squeeze kar deta hai.

01map & filter: Transform and Select

map(callback) creates a NEW array by applying a callback to every element. The callback receives (element, index, array).

filter(callback) creates a NEW array with only elements where the callback returns truthy.

Both skip holes (callback not called for empty slots). Both do NOT mutate the original array.

const users = [
  { name: "Sai", active: true },
  { name: "Ali", active: false },
  { name: "Ravi", active: true }
];

// map — transform data
const names = users.map(user => user.name);
console.log(names); // ["Sai", "Ali", "Ravi"]

// filter — select data
const activeUsers = users.filter(user => user.active);
console.log(activeUsers);

// Chaining map and filter
const activeNames = users
  .filter(user => user.active)
  .map(user => user.name.toUpperCase());
console.log(activeNames); // ["SAI", "RAVI"]

// ANTI-PATTERN: map for side effects
// const bad = users.map(user => { fetch("/log", user); return user; }); // BAD!
// GOOD: forEach for side effects
// users.forEach(user => fetch("/log", user));
Anti-pattern: Using map for side effects (e.g., fetching data inside map). map returns a new array — if you ignore the return value, you are wasting memory. Use forEach when you only need side effects.
02reduce: The Swiss Army Knife

reduce(callback, initialValue) accumulates array elements into a single value. The callback receives (accumulator, currentValue, index, array).

Without initialValue: Uses first element as initial, starts from index 1. Empty array without initialValue throws TypeError!

The accumulator can be anything: number, string, array, or object.

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

// Sum
const sum = nums.reduce((acc, cur) => acc + cur, 0);
console.log(sum); // 15

// Find Max
const max = nums.reduce((a, b) => (a > b ? a : b), -Infinity);

// Grouping objects by property
const items = [
  { type: "food", name: "Biryani" },
  { type: "drink", name: "Chai" },
  { type: "food", name: "Haleem" }
];
const grouped = items.reduce((acc, item) => {
  if (!acc[item.type]) acc[item.type] = [];
  acc[item.type].push(item.name);
  return acc;
}, {});
console.log(grouped);
// { food: ["Biryani", "Haleem"], drink: ["Chai"] }
Always provide initialValue: Without it, reduce uses the first element as the starting accumulator and skips it. On an empty array, this throws TypeError: Reduce of empty array with no initial value. Provide 0 for sums, [] for arrays, {} for objects.
03find, findIndex, some, every

find(callback): Returns FIRST element that matches. Stops iterating after finding. Returns undefined if not found.

findIndex(callback): Same as find, but returns index (-1 if not found).

some(callback): Returns true if ANY element passes. Short-circuits on first true.

every(callback): Returns true if ALL elements pass. Short-circuits on first false.

Efficiency tip: These methods are more efficient than filter when you only need one match. filter always scans the entire array; find stops at the first match.
const users = [
  { id: 1, name: "Sai", age: 25 },
  { id: 2, name: "Ali", age: 17 },
  { id: 3, name: "Ravi", age: 30 }
];

// find — get the first match
const adult = users.find(u => u.age >= 18);
console.log(adult); // { id: 1, name: "Sai"... }

// findIndex — get the index
const minorIdx = users.findIndex(u => u.age < 18);
console.log(minorIdx); // 1

// some — is there at least one?
const hasMinor = users.some(u => u.age < 18);
console.log(hasMinor); // true

// every — are they all adults?
const allAdults = users.every(u => u.age >= 18);
console.log(allAdults); // false
04flat & flatMap: Unnesting Arrays

flat(depth): Flattens nested arrays up to specified depth. Default depth is 1.

flatMap(callback): Maps then flattens 1 level. More efficient than map().flat().

flat(Infinity) fully flattens any depth. Holes are removed during flattening.

// flat — default depth 1
const nested = [1, [2, 3], [4, [5, 6]]];
console.log(nested.flat());    // [1, 2, 3, 4, [5, 6]]
console.log(nested.flat(2));  // [1, 2, 3, 4, 5, 6]
console.log(nested.flat(Infinity)); // [1, 2, 3, 4, 5, 6]

// flatMap — map + flat(1) in one step
const sentences = ["Hello World", "Good Morning"];
const words = sentences.flatMap(s => s.split(" "));
console.log(words); // ["Hello", "World", "Good", "Morning"]

// Filtering and mapping simultaneously with flatMap
const result = [1, -2, 3, -4].flatMap(n => n > 0 ? [n * 2] : []);
console.log(result); // [2, 6]
V8 optimization: V8 optimizes flatMap heavily. Instead of creating an intermediate array from map() and then flattening it, flatMap() directly allocates the result array of the correct size (if possible) and pushes elements into it, saving memory and GC pressure.
05Anti-patterns: O(n²) & Wasted Arrays

ANTI-PATTERN 1: Using [...acc, item] inside reduce. This creates a new array every iteration, making it O(n²). Use push or filter instead.

ANTI-PATTERN 2: Chaining 10+ methods. Each step creates a new intermediate array. Use a single for-loop for performance-critical code.

ANTI-PATTERN 3: Using map for side effects. map returns an array; if you ignore the return value, use forEach instead.

// ANTI-PATTERN O(n^2): Spread in reduce
const evensBad = nums.reduce((acc, n) => {
  if (n % 2 === 0) return [...acc, n]; // Copies ENTIRE acc every loop!
  return acc;
}, []);

// GOOD O(n): Mutate the accumulator (it is our local array)
const evensGood = nums.reduce((acc, n) => {
  if (n % 2 === 0) acc.push(n);
  return acc;
}, []);

// BEST: Just use filter!
const evensBest = nums.filter(n => n % 2 === 0);

// ANTI-PATTERN: Excessive chaining (5 intermediate arrays!)
const result = data
  .map(transform1)
  .filter(predicate1)
  .map(transform2)
  .filter(predicate2)
  .sort(compare);

// GOOD for perf: Single pass (0 intermediate arrays)
const result = [];
for (const item of data) {
  const t1 = transform1(item);
  if (!predicate1(t1)) continue;
  const t2 = transform2(t1);
  if (!predicate2(t2)) continue;
  result.push(t2);
}
result.sort(compare);
📋 When to optimize: For 99% of web apps, the readability of map/filter/reduce chains is worth the minor performance cost. Only switch to manual for-loops when processing massive datasets (>100,000 items) and you have identified the chain as a bottleneck via profiling.

Lo kar liya — Key Points:

  • ✅ map transforms every element; filter selects elements; both return new arrays and skip holes
  • ✅ reduce accumulates array elements into a single value; always provide an initialValue to avoid bugs
  • ✅ find, some, and every short-circuit — they stop iterating as soon as the result is known
  • ✅ flat flattens nested arrays; flatMap maps and then flattens one level (more efficient than map().flat())
  • ✅ Using [...acc, item] inside reduce is O(n²) — use acc.push() or just use filter()
  • ✅ Use map for transformations, forEach for side effects; never use map if you ignore its return value
Course Search
Search across all chapters & stages
📖

Search the course

Type any topic — branching, stash, rebase, hooks — and jump straight to that chapter.

merge branchesgit stashundo commitrebase