skills/software-engineering/fp-higher-order-functions/SKILL.md
Master higher-order functions, map/filter/reduce patterns for expressive data transformations
npx skillsauth add pantheon-org/tekhne fp-higher-order-functionsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Higher-order functions are functions that take other functions as arguments or return functions as results. They enable powerful abstractions, code reuse, and declarative programming styles. The classic trio of map, filter, and reduce form the foundation of functional data transformation.
A higher-order function is any function that does at least one of the following:
This enables composition, abstraction, and powerful declarative patterns.
// Function that takes a function as argument
function applyOperation(x, y, operation) {
return operation(x, y);
}
const sum = applyOperation(5, 3, (a, b) => a + b); // 8
const product = applyOperation(5, 3, (a, b) => a * b); // 15
// Function that returns a function
function createMultiplier(factor) {
return function(number) {
return number * factor;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
// Practical example: Event handler creator
function createClickHandler(message) {
return function(event) {
console.log(message, event.target);
};
}
const submitHandler = createClickHandler('Form submitted from:');
const cancelHandler = createClickHandler('Cancelled from:');
// Higher-order function for timing
function measureTime(fn) {
return function(...args) {
const start = performance.now();
const result = fn(...args);
const end = performance.now();
console.log(`Execution time: ${end - start}ms`);
return result;
};
}
function slowCalculation(n) {
let sum = 0;
for (let i = 0; i < n; i++) {
sum += i;
}
return sum;
}
const timedCalculation = measureTime(slowCalculation);
timedCalculation(1000000); // Logs execution time
Map applies a function to each element in a collection, returning a new collection.
const numbers = [1, 2, 3, 4, 5];
// Basic map
const doubled = numbers.map(n => n * 2);
// [2, 4, 6, 8, 10]
// Map with objects
const users = [
{ id: 1, firstName: 'John', lastName: 'Doe' },
{ id: 2, firstName: 'Jane', lastName: 'Smith' },
{ id: 3, firstName: 'Bob', lastName: 'Johnson' }
];
const fullNames = users.map(user => `${user.firstName} ${user.lastName}`);
// ['John Doe', 'Jane Smith', 'Bob Johnson']
const userIds = users.map(user => user.id);
// [1, 2, 3]
// Map to different structure
const userCards = users.map(user => ({
id: user.id,
displayName: `${user.firstName} ${user.lastName}`,
initials: `${user.firstName[0]}${user.lastName[0]}`
}));
// Map with index
const indexed = numbers.map((n, i) => ({ value: n, index: i }));
// Chaining maps
const result = numbers
.map(n => n * 2)
.map(n => n + 1)
.map(n => n.toString());
// ['3', '5', '7', '9', '11']
// Practical: Price calculation
const products = [
{ name: 'Widget', price: 10, quantity: 2 },
{ name: 'Gadget', price: 15, quantity: 1 },
{ name: 'Doohickey', price: 7, quantity: 5 }
];
const lineItems = products.map(product => ({
name: product.name,
total: product.price * product.quantity
}));
Filter creates a new collection containing only elements that satisfy a predicate.
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Basic filter
const evens = numbers.filter(n => n % 2 === 0);
// [2, 4, 6, 8, 10]
const greaterThanFive = numbers.filter(n => n > 5);
// [6, 7, 8, 9, 10]
// Filter with objects
const users = [
{ id: 1, name: 'Alice', age: 25, active: true },
{ id: 2, name: 'Bob', age: 30, active: false },
{ id: 3, name: 'Charlie', age: 35, active: true },
{ id: 4, name: 'Diana', age: 28, active: true }
];
const activeUsers = users.filter(user => user.active);
const youngUsers = users.filter(user => user.age < 30);
// Complex predicates
const activeYoungUsers = users.filter(user =>
user.active && user.age < 30
);
// Filter with negation
const inactiveUsers = users.filter(user => !user.active);
// Combining map and filter
const activeUserNames = users
.filter(user => user.active)
.map(user => user.name);
// ['Alice', 'Charlie', 'Diana']
// Practical: Search functionality
function searchUsers(users, query) {
const lowerQuery = query.toLowerCase();
return users.filter(user =>
user.name.toLowerCase().includes(lowerQuery)
);
}
// Remove nulls/undefined
const values = [1, null, 2, undefined, 3, null, 4];
const defined = values.filter(v => v != null);
// [1, 2, 3, 4]
// Or more strictly
const truthy = values.filter(Boolean);
Reduce (also called fold) processes a collection to produce a single value.
const numbers = [1, 2, 3, 4, 5];
// Sum
const sum = numbers.reduce((acc, n) => acc + n, 0);
// 15
// Product
const product = numbers.reduce((acc, n) => acc * n, 1);
// 120
// Maximum
const max = numbers.reduce((acc, n) => Math.max(acc, n), -Infinity);
// Minimum
const min = numbers.reduce((acc, n) => Math.min(acc, n), Infinity);
// Building objects from arrays
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
const userMap = users.reduce((acc, user) => {
acc[user.id] = user;
return acc;
}, {});
// { 1: {id: 1, name: 'Alice'}, 2: {...}, 3: {...} }
// Grouping
const transactions = [
{ category: 'food', amount: 50 },
{ category: 'transport', amount: 30 },
{ category: 'food', amount: 25 },
{ category: 'entertainment', amount: 100 },
{ category: 'transport', amount: 15 }
];
const byCategory = transactions.reduce((acc, transaction) => {
const category = transaction.category;
if (!acc[category]) {
acc[category] = [];
}
acc[category].push(transaction);
return acc;
}, {});
// Count occurrences
const fruits = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'];
const counts = fruits.reduce((acc, fruit) => {
acc[fruit] = (acc[fruit] || 0) + 1;
return acc;
}, {});
// { apple: 3, banana: 2, orange: 1 }
// Flatten arrays
const nested = [[1, 2], [3, 4], [5, 6]];
const flattened = nested.reduce((acc, arr) => acc.concat(arr), []);
// [1, 2, 3, 4, 5, 6]
// Or use flat()
const flattenedModern = nested.flat();
// Composing functions
const compose = (...fns) =>
fns.reduce((f, g) => (...args) => f(g(...args)));
const addOne = x => x + 1;
const double = x => x * 2;
const square = x => x * x;
const composed = compose(square, double, addOne);
console.log(composed(3)); // ((3 + 1) * 2)^2 = 64
// Pipeline (left to right)
const pipe = (...fns) =>
fns.reduce((f, g) => (...args) => g(f(...args)));
const piped = pipe(addOne, double, square);
console.log(piped(3)); // ((3 + 1) * 2)^2 = 64
The guidance above uses JavaScript for the primary examples. For the same patterns in other languages and deeper material, see:
Express what a transformation produces, not how to iterate. Reach for map/filter/reduce and small composed functions before an index loop with a mutable accumulator. Know when not to: a genuinely side-effecting loop (streaming I/O) is clearer left imperative.
for (let i = 0; i < xs.length; i++) { out.push(xs[i] * 2); }const out = xs.map((x) => x * 2);reduce that filters, transforms, and groups at once.filter, then map, then reduce steps (or pipe).xs.map((x) => { x.seen = true; return x; })xs.map((x) => ({ ...x, seen: true }))tools
A skill that produces warnings but no errors.
testing
A well-formed example skill for testing the validator.
development
A skill with code blocks and imperative instructions for testing content and contamination analysis.
tools