Higher-Order Functions in JavaScript
A Higher-Order Function (HOF) is a function that takes another function as an argumentt, or returns a function as a result. In JavaScript, HOFs are an extremely common concept — all array methods such as map(), filter(), reduce(), and sort() are higher-order functions. Understanding HOFs makes code more declarative, concise, and maintainable.
Functions as Values
In JavaScript, functions are first-class citizens — they can be treated like any ordinary variable.
// A function can be stored in a variable
function add(a, b) {
return a + b;
}
const operation = add;
console.log(operation(3, 5)); // 8
// A function can be passed to another function
function run(fn, a, b) {
return fn(a, b);
}
console.log(run(add, 10, 5)); // 15
console.log(run((a, b) => a * b, 10, 5)); // 50
// A function can be returned from a function
function createDivider(n) {
return function(x) {
return x / n;
};
}
const bagiDua = createDivider(2);
const bagiTiga = createDivider(3);
console.log(bagiDua(10)); // 5
console.log(bagiTiga(10)); // 3.333...
Code Example: Higher-Order Functions
// === HOF: taking a function as an argumentt ===
// 1. Array.prototype.forEach
const courseList = ["Algorithms", "Database", "Web Dev"];
courseList.forEach(function(course, index) {
console.log(`${index + 1}. ${course}`);
});
// 2. Array.prototype.map — transform every element
const numbers = [10, 20, 30, 40, 50];
const timesTwo = numbers.map(n => n * 2);
console.log(timesTwo); // [20, 40, 60, 80, 100]
// 3. Array.prototype.filter — narrow down elements
const even = numbers.filter(n => n % 2 === 0);
console.log(even); // [20, 40]
// 4. Array.prototype.reduce — accumulate
const total = numbers.reduce((acc, n) => acc + n, 0);
console.log(total); // 150
// 5. Array.prototype.sort — sort
const grade = [85, 92, 78, 90, 88];
const sorted = [...grade].sort((a, b) => b - a);
console.log(sorted); // [92, 90, 88, 85, 78]
// === HOF: returning a function ===
// Function composition
function compose(f, g) {
return function(x) {
return f(g(x));
};
}
const timesTwo = n => n * 2;
const addFive = n => n + 5;
const timesTwoPlusFive = compose(addFive, timesTwo);
console.log(timesTwoPlusFive(3)); // 11 (3*2=6, 6+5=11)
Array Methods Revisited: Complex Logic
const students = [
{ name: "John", majorCode: "CS", gpa: 3.75, semester: 4, credits: 13 },
{ name: "Amy", majorCode: "IS", gpa: 3.90, semester: 4, credits: 14 },
{ name: "Mike", majorCode: "CS", gpa: 2.80, semester: 2, credits: 12 },
{ name: "Anna", majorCode: "IS", gpa: 3.50, semester: 4, credits: 15 },
{ name: "David", majorCode: "CE", gpa: 3.20, semester: 6, credits: 11 },
{ name: "Mary", majorCode: "CS", gpa: 3.85, semester: 2, credits: 14 },
{ name: "Rudy", majorCode: "IS", gpa: 2.90, semester: 6, credits: 10 },
];
// === FILTER with multiple conditions ===
const calonCumLaude = students.filter(student =>
student.gpa >= 3.5 && student.semester <= 4
);
console.log("Calon Cum Laude:", calonCumLaude.map(m => m.name));
// === MAP for transformation ===
const reportCard = students.map(student => ({
...student,
classification: student.gpa >= 3.5 ? "Cum Laude" :
student.gpa >= 3.0 ? "Very Satisfactory" :
student.gpa >= 2.5 ? "Memuaskan" : "Cukup",
totalCost: student.credits * 200000
}));
console.log(reportCard);
// === REDUCE for aggregation ===
// Grouping by majorCode
const groupingByMajor = students.reduce((acc, student) => {
if (!acc[student.majorCode]) {
acc[student.majorCode] = [];
}
acc[student.majorCode].push(student);
return acc;
}, {});
console.log(groupingByMajor);
// Statistics per majorCode
const statsPerMajor = Object.entries(groupingByMajor).map(([majorCode, student]) => {
const averageGPA = student.reduce((acc, m) => acc + m.gpa, 0) / student.length;
const totalCredits = student.reduce((acc, m) => acc + m.credits, 0);
return {
majorCode,
countStudent: student.length,
averageGPA: parseFloat(averageGPA.toFixed(2)),
totalCredits
};
});
console.log(statsPerMajor);
// === SORT with complex logic ===
const ranking = [...students]
.sort((a, b) => {
// Primary: GPA descending
if (b.gpa !== a.gpa) return b.gpa - a.gpa;
// Secondary: credits descending (more credits is better)
return b.credits - a.credits;
})
.map((student, i) => ({
rank: i + 1,
name: student.name,
majorCode: student.majorCode,
gpa: student.gpa
}));
console.log("Ranking Student:");
ranking.forEach(r => console.log(`#${r.rank} ${r.name} (${r.majorCode}) — GPA: ${r.gpa}`));
Code Explanation
| Method | Callback Function | Return |
|---|---|---|
forEach(cb) |
Executed for every element | undefined |
map(cb) |
Transforms every element | New array |
filter(cb) |
Filters the elements that pass | New array |
reduce(cb, init) |
Accumulates into a single value | Single value |
sort(cb) |
Sorts based on the comparator | Sorted array (mutated!) |
find(cb) |
Finds the first matching element | Element or undefined |
some(cb) |
Checks whether any element matches | Boolean |
every(cb) |
Checks whether all elements match | Boolean |
Chaining tips:
array
.filter(...) → filtering
.map(...) → transformation
.sort(...) → sorting
.reduce(...) → accumulation
Real-World Examples
1. Filtering & Sorting a Course List
const courseCatalog = [
{ code: "TI201", name: "Algorithms & Data Structures", credits: 3, semester: 2, lecturer: "Dr. Aaron", price: 200000, rating: 4.8 },
{ code: "TI301", name: "Web Programming", credits: 4, semester: 3, lecturer: "Dr. Sarah", price: 250000, rating: 4.6 },
{ code: "SI201", name: "Database", credits: 3, semester: 2, lecturer: "Dr. John", price: 200000, rating: 4.5 },
{ code: "TI401", name: "Artificial Intelligence", credits: 3, semester: 4, lecturer: "Dr. Anna", price: 300000, rating: 4.9 },
{ code: "SI301", name: "Data Mining", credits: 4, semester: 3, lecturer: "Dr. Mary", price: 280000, rating: 4.3 },
{ code: "TK301", name: "Computer Networking", credits: 3, semester: 3, lecturer: "Dr. David", price: 250000, rating: 4.7 },
{ code: "TI501", name: "Machine Learning", credits: 4, semester: 5, lecturer: "Dr. Aaron", price: 350000, rating: 4.9 },
{ code: "SI401", name: "Geographic Information Systems", credits: 3, semester: 4, lecturer: "Dr. Sarah", price: 300000, rating: 4.2 },
];
// Multi-criteria filtering function
function filterCourse(courseCatalog, filter) {
return courseCatalog.filter(course => {
if (filter.majorCode && !course.code.startsWith(filter.majorCode)) return false;
if (filter.minCredits && course.credits < filter.minCredits) return false;
if (filter.maxPrice && course.price > filter.maxPrice) return false;
if (filter.minRating && course.rating < filter.minRating) return false;
if (filter.semester && course.semester !== filter.semester) return false;
return true;
});
}
// Flexible sorting function
function sortCourse(courseCatalog, sortBy, direction = "asc") {
return [...courseCatalog].sort((a, b) => {
const valA = a[sortBy];
const valB = b[sortBy];
const mod = direction === "asc" ? 1 : -1;
if (typeof valA === "string") {
return valA.localeCompare(valB) * mod;
}
return (valA - valB) * mod;
});
}
// Usage examples
// 1. CS courses with rating >= 4.5
const courseUnggulan = filterCourse(courseCatalog, {
majorCode: "CS",
minRating: 4.5
});
console.log("Course TI Unggulan:");
courseUnggulan.forEach(m =>
console.log(` ${m.name} — ⭐ ${m.rating}`)
);
// 2. Cheapest courses with at least 3 credits
const courseHemat = sortCourse(
filterCourse(courseCatalog, { minCredits: 3, maxPrice: 250000 }),
"price",
"asc"
);
console.log("\numCourses Hemat:");
courseHemat.forEach(m =>
console.log(` ${m.name} — $ ${m.price.toLocaleString("en-US")}`)
);
// 3. Summary per semester
const summaryPerSemester = courseCatalog
.reduce((acc, course) => {
const sem = course.semester;
if (!acc[sem]) {
acc[sem] = { course: [], totalCredits: 0, totalCost: 0 };
}
acc[sem].course.push(course.name);
acc[sem].totalCredits += course.credits;
acc[sem].totalCost += course.price;
return acc;
}, {});
Object.entries(summaryPerSemester).forEach(([sem, data]) => {
console.log(`\nSemester ${sem}:`);
console.log(` Course: ${data.course.join(", ")}`);
console.log(` Total Credits: ${data.totalCredits}`);
console.log(` Total Cost: $ ${data.totalCost.toLocaleString("en-US")}`);
});
Conclusion
- A Higher-Order Function takes or returns a function — a fundamental JavaScript concept.
- Functions in JavaScript are first-class citizens — they can be stored, passed around, and returned.
- Array methods (
map,filter,reduce,sort) are the most commonly used HOFs. - Chaining enables sequential operations: filter → map → sort → reduce.
- HOFs make code more declarative (stating "what") rather than imperative (stating "how").
- Use HOFs to replace complex nested loops.