पाठ 24 / 25
Trade-offs and Performance Pitfalls
Where FP style costs more than it gives.
Know the costs
Common pitfalls in JavaScript-style FP: long chains of map/filter allocate an intermediate array per step (usually fine; matters in hot paths over large data); spreading inside reduce copies the accumulator every iteration, turning linear work quadratic; deep copying whole state trees instead of path copying; recursion on unbounded input without tail-call optimisation; memoisation that leaks memory or keys on object identity. Readability costs matter too: heavy point-free code, unfamiliar abstractions (monad transformers, custom operators) and mismatched team conventions slow reviews. Measure with a profiler before optimising, and choose the style that your team can read.
Quadratic reduce and a linear fix
Both are pure from the caller's point of view.
type Item = { id: string; value: number };
// Quadratic: copies the growing object on every step
const indexSlow = (items: Item[]) =>
items.reduce<Record<string, Item>>((acc, it) => ({ ...acc, [it.id]: it }), {});
// Linear: local mutation of a fresh object that never escapes half-built
function indexFast(items: Item[]): Record<string, Item> {
const acc: Record<string, Item> = {};
for (const it of items) acc[it.id] = it;
return acc;
}
// Or use a built-in that does the same
const indexBuiltIn = (items: Item[]) =>
Object.fromEntries(items.map(it => [it.id, it] as const));Local mutation is fine
A function that mutates only objects it created itself, and returns them without exposing intermediate states, is still pure from the outside. Purity is about observable behaviour.
त्वरित जाँच: Why can `reduce` with object spread on every step be slow?
- Spread syntax runs network requests
- reduce is not optimised by engines
- It copies the whole accumulator each iteration, making the work quadratic
- Objects cannot be returned from reduce
Answer
It copies the whole accumulator each iteration, making the work quadratic — Copying grows with the accumulator size.