Skip to content
Pipelines: Map, Filter, Reduce
step 1/5

Reading — step 1 of 5

Learn

~3 min readCollections

Pipelines: Map, Filter, Reduce

Loops tell the computer HOW to iterate. filter, map, and reduce state WHAT you want: keep these, transform them, collapse to an answer. Once the shape clicks, most array code becomes a short, readable pipeline — and TypeScript type-checks every stage of it.

The three stages

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

nums.filter(n => n % 2 === 0);   // [2, 4]             keep where true
nums.map(n => n * n);            // [1, 4, 9, 16, 25]  transform each
nums.reduce((a, b) => a + b, 0); // 15                 fold into one value
  • filter returns a NEW array containing the elements for which the callback returned true. Same element type, possibly shorter. The original array is untouched.
  • map returns a NEW array of the callback's return values — always the same length, possibly a different type (nums.map(n => String(n)) is string[]).
  • reduce threads an accumulator through the array: the callback is (accumulator, element) => newAccumulator, and the second argument is the accumulator's starting value. The result is a single value.

Chaining — with the compiler watching

const result: number = nums
    .filter(n => n % 2 === 0)        // number[] — evens: [2, 4]
    .map(n => n * n)                 // number[] — squares: [4, 16]
    .reduce((acc, n) => acc + n, 0); // number   — sum: 20

TypeScript infers the type flowing out of each stage. If your map callback accidentally returned a string, the reduce below it would fail to compile immediately — a mistake cannot silently flow downstream. That is the concrete upgrade over writing the same chain in plain JavaScript.

Trap 1: reduce without an initial value

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

With no initial value, reduce seeds the accumulator from the first element — and an EMPTY array has no first element, so it throws at runtime. Any pipeline whose filter might reject everything must give reduce an explicit initial value.

Trap 2: forgetting to return

Concise arrow bodies return automatically; arrow bodies with braces need an explicit return:

nums.map(n => n * n);             // fine — implicit return
nums.map(n => { n * n; });        // BUG — returns undefined for every element!
nums.map(n => { return n * n; }); // fine — explicit return

The buggy version produces [undefined, undefined, ...], which turns all later arithmetic into NaN.

Your exercise

The starter has the whole pipeline scaffolded with placeholder callbacks:

const result: number = nums
    .filter((x) => true /* TODO: keep only evens */)
    .map((x) => x /* TODO: square it */)
    .reduce((acc, x) => acc /* TODO: add x to the sum */, 0);

Replace the three TODOs: keep only even numbers (x % 2 === 0), square them (x * x), and accumulate the sum (acc + x). The starter already prints result.

Mistakes the grader will catch:

  • Leaving the filter as true: for input 1 2 3 4 5 you would sum the squares of everything (55) instead of just the evens — the expected output is 20.
  • Leaving reduce returning bare acc: everything collapses to 0 regardless of input.
  • Deleting reduce's initial 0: the hidden test 1 3 5 7 filters down to an EMPTY array, and reduce with no initial value crashes at runtime — the expected output there is 0, which only happens with the initial value in place.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…