Docs

README

5.7 Higher-Order Functions

Overview

Higher-order functions are functions that either take other functions as arguments or return functions as their result. They're a fundamental concept in functional programming and are essential for writing clean, reusable, and expressive JavaScript code.


Table of Contents

  1. •What are Higher-Order Functions?
  2. •Functions as Arguments
  3. •Functions as Return Values
  4. •Built-in Array Higher-Order Functions
  5. •map()
  6. •filter()
  7. •reduce()
  8. •forEach()
  9. •find() and findIndex()
  10. •some() and every()
  11. •sort()
  12. •Chaining Methods
  13. •Creating Custom Higher-Order Functions
  14. •Best Practices

What are Higher-Order Functions?

A function is "higher-order" if it:

  1. •Takes one or more functions as arguments, OR
  2. •Returns a function as its result
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│              HIGHER-ORDER FUNCTIONS                      │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                          │
│   Regular Function:                                      │
│   f(value) → result                                     │
│                                                          │
│   Higher-Order (takes function):                         │
│   f(function) → result                                  │
│                                                          │
│   Higher-Order (returns function):                       │
│   f(value) → function                                   │
│                                                          │
│   Both:                                                  │
│   f(function) → function                                │
│                                                          │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Basic Example

// Takes a function as argument
function repeat(n, action) {
  for (let i = 0; i < n; i++) {
    action(i);
  }
}

repeat(3, console.log); // 0, 1, 2

// Returns a function
function greaterThan(n) {
  return function (m) {
    return m > n;
  };
}

const greaterThan10 = greaterThan(10);
console.log(greaterThan10(15)); // true

Functions as Arguments

Passing functions to other functions enables powerful patterns.

Callback Pattern

function fetchData(callback) {
  // Simulate async operation
  setTimeout(() => {
    const data = { id: 1, name: 'Result' };
    callback(data);
  }, 100);
}

fetchData((data) => {
  console.log('Received:', data);
});

Customizable Behavior

function processArray(arr, operation) {
  const result = [];
  for (const item of arr) {
    result.push(operation(item));
  }
  return result;
}

const numbers = [1, 2, 3, 4, 5];

const doubled = processArray(numbers, (n) => n * 2);
const squared = processArray(numbers, (n) => n * n);
const stringified = processArray(numbers, (n) => String(n));

console.log(doubled); // [2, 4, 6, 8, 10]
console.log(squared); // [1, 4, 9, 16, 25]
console.log(stringified); // ["1", "2", "3", "4", "5"]

Functions as Return Values

Returning functions creates flexible, reusable code.

Function Factory

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

Configuration Pattern

function createValidator(rules) {
  return function (value) {
    for (const rule of rules) {
      if (!rule.test(value)) {
        return { valid: false, message: rule.message };
      }
    }
    return { valid: true };
  };
}

const validatePassword = createValidator([
  { test: (v) => v.length >= 8, message: 'Too short' },
  { test: (v) => /[A-Z]/.test(v), message: 'Need uppercase' },
  { test: (v) => /[0-9]/.test(v), message: 'Need number' },
]);

console.log(validatePassword('Abc12345')); // { valid: true }
console.log(validatePassword('abc')); // { valid: false, message: "Too short" }

Built-in Array Higher-Order Functions

JavaScript arrays come with many built-in higher-order methods.

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│           ARRAY HIGHER-ORDER METHODS                      │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│ Method     │ Description                                  │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│ map()      │ Transform each element                       │
│ filter()   │ Keep elements matching condition             │
│ reduce()   │ Combine elements into single value           │
│ forEach()  │ Execute for each (no return)                 │
│ find()     │ First element matching condition             │
│ findIndex()│ Index of first match                         │
│ some()     │ At least one matches?                        │
│ every()    │ All match?                                   │
│ sort()     │ Sort with comparison function                │
│ flatMap()  │ Map then flatten one level                   │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

map()

Creates a new array by transforming each element.

Syntax

array.map(callback(element, index, array));

Examples

const numbers = [1, 2, 3, 4, 5];

// Double each number
const doubled = numbers.map((n) => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10]

// Extract property from objects
const users = [
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 30 },
  { name: 'Charlie', age: 35 },
];

const names = users.map((user) => user.name);
console.log(names); // ["Alice", "Bob", "Charlie"]

// Using index
const indexed = numbers.map((num, index) => `${index}: ${num}`);
console.log(indexed); // ["0: 1", "1: 2", "2: 3", "3: 4", "4: 5"]

Important Notes

  • •Returns a new array (doesn't modify original)
  • •Same length as original array
  • •Use when you need to transform each element

filter()

Creates a new array with elements that pass a test.

Syntax

array.filter(callback(element, index, array));

Examples

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// Even numbers
const evens = numbers.filter((n) => n % 2 === 0);
console.log(evens); // [2, 4, 6, 8, 10]

// Greater than 5
const large = numbers.filter((n) => n > 5);
console.log(large); // [6, 7, 8, 9, 10]

// Filter objects
const users = [
  { name: 'Alice', active: true },
  { name: 'Bob', active: false },
  { name: 'Charlie', active: true },
];

const activeUsers = users.filter((user) => user.active);
console.log(activeUsers); // [{ name: "Alice", active: true }, { name: "Charlie", active: true }]

// Multiple conditions
const filtered = numbers.filter((n) => n > 3 && n < 8);
console.log(filtered); // [4, 5, 6, 7]

Important Notes

  • •Returns a new array
  • •Length may be less than original
  • •Use when you need to select a subset

reduce()

Combines all elements into a single value.

Syntax

array.reduce(callback(accumulator, current, index, array), initialValue);

Visual Representation

[1, 2, 3, 4, 5].reduce((acc, cur) => acc + cur, 0)

Step 1: acc=0,  cur=1  → 0+1  = 1
Step 2: acc=1,  cur=2  → 1+2  = 3
Step 3: acc=3,  cur=3  → 3+3  = 6
Step 4: acc=6,  cur=4  → 6+4  = 10
Step 5: acc=10, cur=5  → 10+5 = 15

Result: 15

Examples

const numbers = [1, 2, 3, 4, 5];

// Sum
const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(sum); // 15

// Product
const product = numbers.reduce((acc, n) => acc * n, 1);
console.log(product); // 120

// Max value
const max = numbers.reduce((acc, n) => (n > acc ? n : acc), -Infinity);
console.log(max); // 5

// Count occurrences
const letters = ['a', 'b', 'a', 'c', 'b', 'a'];
const counts = letters.reduce((acc, letter) => {
  acc[letter] = (acc[letter] || 0) + 1;
  return acc;
}, {});
console.log(counts); // { a: 3, b: 2, c: 1 }

// Flatten array
const nested = [[1, 2], [3, 4], [5]];
const flat = nested.reduce((acc, arr) => acc.concat(arr), []);
console.log(flat); // [1, 2, 3, 4, 5]

// Group by property
const people = [
  { name: 'Alice', dept: 'Engineering' },
  { name: 'Bob', dept: 'Sales' },
  { name: 'Charlie', dept: 'Engineering' },
];

const byDept = people.reduce((acc, person) => {
  const dept = person.dept;
  acc[dept] = acc[dept] || [];
  acc[dept].push(person);
  return acc;
}, {});
console.log(byDept);

Important Notes

  • •Always provide an initial value
  • •Can produce any type of result (number, object, array, etc.)
  • •Most powerful but can be harder to read

forEach()

Executes a function for each element (no return value).

Syntax

array.forEach(callback(element, index, array));

Examples

const numbers = [1, 2, 3, 4, 5];

// Side effect: logging
numbers.forEach((n) => console.log(n));

// Modifying external variable
let total = 0;
numbers.forEach((n) => {
  total += n;
});
console.log(total); // 15

// With index
numbers.forEach((n, i) => {
  console.log(`Index ${i}: ${n}`);
});

Important Notes

  • •Returns undefined
  • •Cannot break out of loop (use regular for if needed)
  • •Best for side effects (logging, DOM updates)

forEach vs map

// Use forEach for side effects
users.forEach((user) => {
  sendEmail(user);
});

// Use map for transformation
const emails = users.map((user) => user.email);

find() and findIndex()

Find the first matching element or its index.

Syntax

array.find(callback(element, index, array));
array.findIndex(callback(element, index, array));

Examples

const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
  { id: 3, name: 'Charlie' },
];

// find() - returns element
const user = users.find((u) => u.id === 2);
console.log(user); // { id: 2, name: "Bob" }

// findIndex() - returns index
const index = users.findIndex((u) => u.id === 2);
console.log(index); // 1

// Not found
const notFound = users.find((u) => u.id === 99);
console.log(notFound); // undefined

const notFoundIndex = users.findIndex((u) => u.id === 99);
console.log(notFoundIndex); // -1

find vs filter

// find - returns first match only
const first = numbers.find((n) => n > 3); // 4

// filter - returns all matches
const all = numbers.filter((n) => n > 3); // [4, 5, 6, ...]

some() and every()

Test if elements match a condition.

Syntax

array.some(callback(element, index, array)); // At least one?
array.every(callback(element, index, array)); // All?

Examples

const numbers = [1, 2, 3, 4, 5];

// some() - at least one matches
console.log(numbers.some((n) => n > 3)); // true
console.log(numbers.some((n) => n > 10)); // false

// every() - all match
console.log(numbers.every((n) => n > 0)); // true
console.log(numbers.every((n) => n > 3)); // false

// Practical example
const users = [
  { name: 'Alice', verified: true },
  { name: 'Bob', verified: true },
  { name: 'Charlie', verified: false },
];

const allVerified = users.every((u) => u.verified);
console.log(allVerified); // false

const anyVerified = users.some((u) => u.verified);
console.log(anyVerified); // true

Short-Circuit Behavior

// some() stops at first true
[1, 2, 3].some((n) => {
  console.log('Checking', n);
  return n === 2;
});
// Logs: "Checking 1", "Checking 2" (stops)

// every() stops at first false
[1, 2, 3].every((n) => {
  console.log('Checking', n);
  return n < 2;
});
// Logs: "Checking 1", "Checking 2" (stops)

sort()

Sorts array in place using a comparison function.

Syntax

array.sort(compareFunction(a, b));

Comparison Function Rules

compareFunction(a, b):
  < 0  → a comes before b
  = 0  → keep original order
  > 0  → b comes before a

Examples

const numbers = [3, 1, 4, 1, 5, 9, 2, 6];

// Ascending (numeric)
numbers.sort((a, b) => a - b);
console.log(numbers); // [1, 1, 2, 3, 4, 5, 6, 9]

// Descending (numeric)
numbers.sort((a, b) => b - a);
console.log(numbers); // [9, 6, 5, 4, 3, 2, 1, 1]

// Strings (alphabetical)
const names = ['Charlie', 'Alice', 'Bob'];
names.sort(); // Default alphabetical
console.log(names); // ["Alice", "Bob", "Charlie"]

// Case-insensitive
const mixed = ['banana', 'Apple', 'cherry'];
mixed.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
console.log(mixed); // ["Apple", "banana", "cherry"]

// Sort by object property
const users = [
  { name: 'Alice', age: 30 },
  { name: 'Bob', age: 25 },
  { name: 'Charlie', age: 35 },
];

users.sort((a, b) => a.age - b.age);
console.log(users); // Bob, Alice, Charlie

Important Notes

  • •Modifies original array
  • •Default sort converts to strings
  • •Always use compare function for numbers

Chaining Methods

Combine methods for complex operations.

const transactions = [
  { type: 'sale', amount: 100 },
  { type: 'refund', amount: -50 },
  { type: 'sale', amount: 200 },
  { type: 'sale', amount: 75 },
  { type: 'refund', amount: -25 },
];

// Get total of sales over $50
const result = transactions
  .filter((t) => t.type === 'sale') // Only sales
  .filter((t) => t.amount > 50) // Over $50
  .map((t) => t.amount) // Get amounts
  .reduce((sum, a) => sum + a, 0); // Sum them

console.log(result); // 300

// Find users, extract and format names
const users = [
  { name: 'alice', active: true },
  { name: 'bob', active: false },
  { name: 'charlie', active: true },
];

const activeNames = users
  .filter((u) => u.active)
  .map((u) => u.name)
  .map((name) => name.charAt(0).toUpperCase() + name.slice(1))
  .join(', ');

console.log(activeNames); // "Alice, Charlie"

Chain Visualization

transactions
    │
    ā–¼  filter(type === 'sale')
[sale:100, sale:200, sale:75]
    │
    ā–¼  filter(amount > 50)
[sale:100, sale:200, sale:75]
    │
    ā–¼  map(amount)
[100, 200, 75]
    │
    ā–¼  reduce(sum)
375

Creating Custom Higher-Order Functions

Custom map

function myMap(array, transform) {
  const result = [];
  for (const item of array) {
    result.push(transform(item));
  }
  return result;
}

myMap([1, 2, 3], (n) => n * 2); // [2, 4, 6]

Custom filter

function myFilter(array, predicate) {
  const result = [];
  for (const item of array) {
    if (predicate(item)) {
      result.push(item);
    }
  }
  return result;
}

myFilter([1, 2, 3, 4], (n) => n % 2 === 0); // [2, 4]

Custom reduce

function myReduce(array, reducer, initial) {
  let accumulator = initial;
  for (const item of array) {
    accumulator = reducer(accumulator, item);
  }
  return accumulator;
}

myReduce([1, 2, 3], (a, b) => a + b, 0); // 6

Pipe Function

function pipe(...fns) {
  return function (value) {
    return fns.reduce((acc, fn) => fn(acc), value);
  };
}

const process = pipe(
  (n) => n * 2,
  (n) => n + 1,
  (n) => n * 3
);

console.log(process(5)); // ((5 * 2) + 1) * 3 = 33

Best Practices

1. Use Descriptive Names

// āŒ Unclear
const x = users.filter((u) => u.a > 18);

// āœ… Clear
const adultUsers = users.filter((user) => user.age > 18);

2. Prefer Method Chaining Over Loops

// āŒ Imperative
let result = [];
for (let i = 0; i < users.length; i++) {
  if (users[i].active) {
    result.push(users[i].name.toUpperCase());
  }
}

// āœ… Declarative
const result = users
  .filter((user) => user.active)
  .map((user) => user.name.toUpperCase());

3. Don't Modify Original Array

// āŒ Mutation
numbers.sort();

// āœ… Copy first
const sorted = [...numbers].sort();

4. Use the Right Method

NeedUse
Transform allmap()
Select somefilter()
Single valuereduce()
First matchfind()
Existence checksome()/every()
Side effectsforEach()

5. Keep Callbacks Pure

// āŒ Side effects in map
let total = 0;
numbers.map((n) => {
  total += n; // Side effect!
  return n * 2;
});

// āœ… Use reduce for accumulation
const total = numbers.reduce((sum, n) => sum + n, 0);
const doubled = numbers.map((n) => n * 2);

Summary

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│             HIGHER-ORDER FUNCTIONS                        │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                           │
│  DEFINITION:                                              │
│  • Takes function(s) as argument(s), OR                   │
│  • Returns a function                                     │
│                                                           │
│  KEY METHODS:                                             │
│  map()      - Transform each → new array                 │
│  filter()   - Select matching → new array                │
│  reduce()   - Combine all → single value                 │
│  forEach()  - Execute for each → undefined               │
│  find()     - First match → element                      │
│  findIndex()- First match → index                        │
│  some()     - Any match? → boolean                       │
│  every()    - All match? → boolean                       │
│  sort()     - Sort in place → same array                 │
│                                                           │
│  BENEFITS:                                                │
│  • Declarative code                                       │
│  • Reusable logic                                         │
│  • Composable operations                                  │
│  • Easier to reason about                                 │
│                                                           │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Next Steps

  • •Practice with the examples in examples.js
  • •Complete the exercises in exercises.js
  • •Explore functional programming concepts
  • •Learn about recursion and advanced patterns
README - JavaScript Tutorial | DeepML