JavaScript Curriculum
Transforming Data
mediumA Nexus dashboard needs to display active users sorted by XP, with names formatted for display. The imperative version has three for loops, four temporary arrays, and a bug nobody found for two weeks. The functional version is a single chain: filter active users, sort by XP, map to display format. The intent is clear, the data flow is visible, and the bug can't hide.
Imperative vs declarative
Imperative code tells JavaScript how to do something step by step. Declarative code tells it what you want. Functional programming is the declarative style.
Both produce the same result. The declarative version states the intent in one line, doesn't mutate anything, and has no loop bookkeeping to get wrong.
map, filter, reduce — the core three
Watch real data flow through a pipeline:
Nexus Pro
$49.99 · ✓
Pulse
$129.00 · ✓
Orbit
$9.99 · ✗
Vega
$2.50 · ✓
Flare
$79.00 · ✗
Core
$19.99 · ✓
.filter(fn) — keep or discard
Runs the function on every element. Keeps elements where the function returns true. Returns a new array — original unchanged.
.map(fn) — transform every element
Runs the function on every element. Returns a new array of the transformed values — same length as the input.
.reduce(fn, initial) — accumulate to a single value
The most powerful and flexible. Runs the function on each element, carrying a running accumulator forward.
Chaining — pipelines
The real power comes from chaining multiple operations:
Each method returns a new array, so the next method can be called immediately. The data flows top to bottom — no intermediate variables, no mutation.
Pure functions — the foundation
A pure function has two properties: (1) same input always returns same output, (2) no side effects — doesn't modify anything outside itself.
function applyDiscount(price, discount) {
return price * (1 - discount) // only uses arguments
// no external state touched
}✓ Pure — same input always gives same output
Functional programming is built on pure functions because they're:
- Testable — no setup needed, no mocks required
- Predictable — you can reason about them in isolation
- Composable — safe to chain because they don't interfere with each other
- Cacheable — same input → same output → result can be memoized
Function composition
Small pure functions combine into larger ones:
Useful array methods beyond the core three
Your challenge
getTopScorers is a three-step pipeline in the right order: sort first (so map produces the right sequence), then filter (eliminate below threshold), then map to strings. The sort comparator (a, b) => b.score - a.score is the standard descending-sort pattern worth memorising — positive result means b comes first.
Challenge
Write a function called getTopScorers(users, minScore) that takes an array of user objects and a minimum score. Use .filter() to keep only users with score >= minScore. Use .map() to transform each user to the string user.name + ': ' + user.score. Use .sort() (before map) to sort descending by score. Return the resulting array of strings. Test with: [{ name: 'Alex', score: 95 }, { name: 'Jordan', score: 72 }, { name: 'Sam', score: 88 }, { name: 'Mia', score: 60 }] and minScore 75.
Transforming Data
mediumA Nexus dashboard needs to display active users sorted by XP, with names formatted for display. The imperative version has three for loops, four temporary arrays, and a bug nobody found for two weeks. The functional version is a single chain: filter active users, sort by XP, map to display format. The intent is clear, the data flow is visible, and the bug can't hide.
Imperative vs declarative
Imperative code tells JavaScript how to do something step by step. Declarative code tells it what you want. Functional programming is the declarative style.
Both produce the same result. The declarative version states the intent in one line, doesn't mutate anything, and has no loop bookkeeping to get wrong.
map, filter, reduce — the core three
Watch real data flow through a pipeline:
Nexus Pro
$49.99 · ✓
Pulse
$129.00 · ✓
Orbit
$9.99 · ✗
Vega
$2.50 · ✓
Flare
$79.00 · ✗
Core
$19.99 · ✓
.filter(fn) — keep or discard
Runs the function on every element. Keeps elements where the function returns true. Returns a new array — original unchanged.
.map(fn) — transform every element
Runs the function on every element. Returns a new array of the transformed values — same length as the input.
.reduce(fn, initial) — accumulate to a single value
The most powerful and flexible. Runs the function on each element, carrying a running accumulator forward.
Chaining — pipelines
The real power comes from chaining multiple operations:
Each method returns a new array, so the next method can be called immediately. The data flows top to bottom — no intermediate variables, no mutation.
Pure functions — the foundation
A pure function has two properties: (1) same input always returns same output, (2) no side effects — doesn't modify anything outside itself.
function applyDiscount(price, discount) {
return price * (1 - discount) // only uses arguments
// no external state touched
}✓ Pure — same input always gives same output
Functional programming is built on pure functions because they're:
- Testable — no setup needed, no mocks required
- Predictable — you can reason about them in isolation
- Composable — safe to chain because they don't interfere with each other
- Cacheable — same input → same output → result can be memoized
Function composition
Small pure functions combine into larger ones:
Useful array methods beyond the core three
Your challenge
getTopScorers is a three-step pipeline in the right order: sort first (so map produces the right sequence), then filter (eliminate below threshold), then map to strings. The sort comparator (a, b) => b.score - a.score is the standard descending-sort pattern worth memorising — positive result means b comes first.
Challenge
Write a function called getTopScorers(users, minScore) that takes an array of user objects and a minimum score. Use .filter() to keep only users with score >= minScore. Use .map() to transform each user to the string user.name + ': ' + user.score. Use .sort() (before map) to sort descending by score. Return the resulting array of strings. Test with: [{ name: 'Alex', score: 95 }, { name: 'Jordan', score: 72 }, { name: 'Sam', score: 88 }, { name: 'Mia', score: 60 }] and minScore 75.