Skillversitas

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.