028
LVL 01 — WAVE DEFENDER SESSION 028 DAY 28

HIGHER-ORDER FUNCTIONS

🎫 PIXELCRAFT-016
🔧 Refactor | 🟡 Medium | Priority: 🟡 Medium

Code review found the same pixel-processing pattern duplicated in 6 different places. Bug fixed in one spot was missed in others. Refactor using higher-order functions.
CONCEPTS.UNLOCKED
ƒ(ƒ)
Higher-Order Functions
Functions that accept or return other functions. You already used one: addEventListener('click', handler) — it takes a function as an argument.
🗺
Array.map()
Transform every element → new array. [1,2,3].map(x => x * 2) → [2,4,6]. The original array is untouched. Pure transformation.
🔍
Array.filter()
Keep elements matching a condition → new array. [1,2,3,4,5].filter(x => x > 3) → [4,5]. Returns a subset, never mutates the original.
📦
Array.reduce()
Combine all elements into one result. [1,2,3].reduce((sum, x) => sum + x, 0) → 6. The most powerful array method — map and filter can both be built from reduce.
🔗
Function Composition
pipeline(data, [fn1, fn2, fn3]) — chain small functions together. Each function transforms data and passes it to the next. The output of one is the input to the next.
📞
Callback Pattern
Passing functions as arguments. applyFilter(data, grayscale) — the loop doesn't know which filter, it just calls whatever function you pass. Maximum flexibility.
HANDS-ON.TASKS
01
See the Duplication

The pixel loop is in 6 places with slight variations. Each has the same getImageData → loop → clamp → putImageData pattern. A bug fixed in one spot was missed in the other 5.

This is exactly the DRY violation from Session 22 — except now it's grown from 4 copies to 6. The problem gets worse with every feature added.
02
Create a Composable Filter Pipeline
function createFilterPipeline(filters) { return function(r, g, b) { let result = [r, g, b]; for (const filter of filters) { result = filter(...result); } return result; }; }
This is a higher-order function that returns a function. createFilterPipeline takes an array of filters and produces a single function that runs them all in sequence.
03
Use reduce() for the Pipeline
function applyPipeline(imageData, filterFns) { const pixels = imageData.data; const pipeline = (r, g, b) => filterFns.reduce( ([r, g, b], fn) => fn(r, g, b), [r, g, b] ); for (let i = 0; i < pixels.length; i += 4) { const [newR, newG, newB] = pipeline( pixels[i], pixels[i+1], pixels[i+2] ); pixels[i] = clamp(newR); pixels[i + 1] = clamp(newG); pixels[i + 2] = clamp(newB); } }
reduce() walks through the filter array, passing the [r,g,b] result of each filter as input to the next. The initial value [r,g,b] is the starting pixel color.
04
Build Active Filter List Dynamically
function getActiveFilters(state) { const filters = []; if (state.brightness !== 0) filters.push((r,g,b) => brightness(r,g,b, state.brightness)); if (state.contrast !== 1) filters.push((r,g,b) => contrast(r,g,b, state.contrast)); if (state.grayscale) filters.push(grayscale); if (state.invert) filters.push(invert); return filters; }

Only active filters are included. The pipeline is built fresh on every render — no wasted computation on disabled filters.

05
Refactor applyAllFilters()

Replace the Session 27 applyAllFilters() with the pipeline approach. The old function had hardcoded if-checks for each filter. The new one uses getActiveFilters() + applyPipeline().

06
Use map() & filter() on Non-Pixel Data

Demonstrate Array.map() and Array.filter() on filter settings and gallery items — these methods aren't just for pixels:

// Get names of all active filters const activeNames = Object.entries(filterState) .filter(([key, val]) => val !== 0 && val !== 1 && val !== false) .map(([key, val]) => `${key}: ${val}`); // ["brightness: 50", "contrast: 1.5"]
07
Close the Ticket
git switch -c refactor/PIXELCRAFT-016-higher-order-functions git add src/scripts/ git commit -m "Refactor filters to composable pipeline with HOFs (PIXELCRAFT-016)" git push origin refactor/PIXELCRAFT-016-higher-order-functions # PR → Review → Merge → Close ticket ✅
CS.DEEP-DIVE

Functional programming and composition.

Small, pure functions composed into powerful pipelines. This is the Unix philosophy in code.

// Unix pipes — same concept:

cat file | grep error | sort | uniq -c | head -5

// Each tool does ONE thing.
// Piped together, they solve complex problems.

// Your filter pipeline:

pixel → brightnesscontrastgrayscale → display

// Function composition applied to
// image processing. This thinking pattern
// scales from small utilities to entire
// system architectures.
"Composition Playground"
[A] Write a general-purpose compose() function: compose(f, g)(x) = f(g(x)). Use it to compose string transformations: compose(toUpper, trim, removeSpaces).
[B] Use .reduce() to calculate the total file size of recentFiles, the average brightness across all settings presets, and the longest filename.
[C] Create a "Filter Presets" feature: save the current filterState as a named preset (e.g., "Vintage", "High Contrast B&W"). Load a preset → instantly apply all its filters.
REF.MATERIAL
VIDEO
Web Dev Simplified
Practical walkthrough of map, filter, reduce, find, some, every, forEach, and includes with real-world examples.
MAP/FILTER/REDUCEESSENTIAL
VIDEO
Fun Fun Function
The best explanation of higher-order functions: what they are, why they matter, and how map/filter/reduce transform the way you think about data.
HOFFUNCTIONAL
ARTICLE
Mozilla Developer Network
Complete reduce() reference with accumulator patterns, initial values, and advanced examples like flattening and grouping.
REDUCEMDNREFERENCE
ARTICLE
javascript.info
Interactive deep dive into all array methods with runnable examples. The "functional trio" of map, filter, reduce covered in depth.
ARRAYSINTERACTIVE
ARTICLE
Wikipedia
The philosophy behind composition: "Do one thing and do it well." How small, focused tools piped together solve complex problems.
UNIXPHILOSOPHYCS
// LEAVE EXCITED BECAUSE
6 copies of the same code became 1 composable pipeline. Adding a new filter is now one function. This is elegant engineering.