JavaScript higher-order functions

performing more powerful functions such as transforming and filtering arrays
// updated 2025-05-06 16:06

With functions we learned that we could pass in variables as "parameters", but with higher-order functions we can pass in other functions! Some built-in higher-order functions in JavaScript include:

  • forEach()
  • map()
  • filter()
  • reduce()
  • sort()
  • every()
  • some()

forEach

Suppose we have an array and we wish to iterate through each of its elements:

We could use a for loop, or we could use a cleaner, high-order forEach method:

const grades = [58, 60, 60, 90, 99]

// we could do this
for (let i = 0; i < grades.length; i++) {
  console.log(grades[i] + 10)
}

// or we could do this
grades.forEach(function (grade) {
  console.log(grade + 10)
})

// we could even do this to look even "more skilled"
grades.forEach(grade => console.log(grade + 10))

In the example above, notice how each block looks progressively simpler but does the same thing as the others!

Note that the forEach() higher-order function:

  • always returns undefined
  • iterates through each element and calls other functions to output a result
  • does not modify the original array
  • does not create a new array
  • does not allow the use of a break

map

Suppose we have an array and we wish to go through its elements, then perform the same operation on those elements...

We could use the built-in map() function to iterate and transform:

// an array of grades in a class
const grades = [72, 75, 90, 91, 92, 92, 93, 94]

// everyone's grades go up by 10%
const bellCurve = grades.map((grade) => grade + 5)

console.log(grades) 
// [72, 75, 90, 91, 92, 92, 93, 94]
// i.e. the original array does not change

console.log(bellCurve)
// [77, 80, 95, 96, 97, 97, 98, 99]
// the map creates a new array, separate from the "real world"

Note that map():

  • returns the values into a new array
    • the length of the new array will always be equal to the original array
  • the original array stays the same

filter

Suppose we have an array and we wish to go through its elements, then perform remove elements that do not satisfy some condition...

We could use the built-in filter() function to iterate and eliminate:

// an array of grades in a class
const grades = [72, 75, 90, 91, 92, 92, 93, 94]

// the callback function gets only "grades 90 and up"
const honors = grades.filter((grade) => grade >= 90)

console.log(grades) 
// [72, 75, 90, 91, 92, 92, 93, 94]
// i.e. the original array does not change

console.log(bellCurve)
// [90, 91, 92, 92, 93, 94]
// the filter removes any values under 90 by the return value in the callback

Note that filter():

  • returns the values into a new array
    • the length of this array will always be equal or less than the original array
    • the original array stays the same

reduce

Suppose we have an array and we wish to find the sum of all its values...

We could use the built-in reduce() function to iterate and summarize:

// an array of grades in a class
const grades = [72, 75, 90, 91, 92, 92, 93, 94]

// the callback function gets only "grades 90 and up"
const gradeSum = grades.reduce((accumulator, currentGrade) => 
  accumulator += currentGrade 
) 

const average = gradeSum / grades.length

console.log(grades) 
// [72, 75, 90, 91, 92, 92, 93, 94]
// i.e. the original array does not change

console.log(average)
// 87.375 (i.e. 699 / 8)

So unlike map() and filter(), the callback function passed into reduce() has two required parameters:

  • accumulator
    • the running total of the function
  • currentValue (shown as currentGrade above)

Note that reduce():

  • returns a single value
    • by accumulating the values (most often by addition) of every element in the array
  • the original array stays the same

sort

Suppose we have an array and we wish to sort its elements...

We could use the built-in sort() function to iterate and re-arrange:

// an array of titles
const titles = ["January", "February", "March", "April"]

// the callback function gets only "grades 90 and up"
const sorted = titles.sort()

console.log(sorted)
// ["April", "February", "March", "January"]

By default, this method sorts crazily by:

  • converting all values into strings
  • and then by this hierarchy:
    • arrays
    • numbers
      • (by digit, i.e. 1 is before 20, 20 is before 21, but 21 is before 9!)
    • strings with uppercase letters
    • objects
      • curly braces fall in between uppercase and lowercase letters!
    • strings with lowercase letters
      • null is considered a string
    • undefined
      • strangely, this is not a string
    • empty slots

So, we can see this more clearly with an example:

const arrayMix = [ [], 99, 7, 'B', 'ZZZZ', 
    'P', NaN, {}, 'a', null, 224, null, undefined, 
    'x', ,'z'
]

console.log(arrayMix.sort())
/*
[ 
    [], 224, 7, 99, 'B', NaN, 'P', 'ZZZZ', {}, 'a', 
    null, null, 'x', 'z', undefined, empty
]    
*/

How do we sort numbers by amount (and not by their first digits)?

We would need to use a comparison function as a callback function when using sort():

const numbers = [25, 1, 118, 20, 92, 444, -1, 2]

// without comparison function
console.log(numbers.sort())
// [-1, 1, 118, 2, 20, 25, 444, 92] (!!!)

// with comparison function
console.log(numbers.sort((a,b) => a-b))
// [-1, 1, 2, 20, 25, 92, 118, 444] :)

This callback function (a, b) => a-b simply:

  • iterates through each pair of elements in the array
  • compares the two values and does its rearranging in-place
    • it creates no new arrays but changes the original array

every and some

Suppose we have an array and we wish to go through its elements, then find out if all elements meet a condition, or at least one does so...

We could use the built-in every() and some() functions to iterate and test for uniformity or existence:

// an array of names in a group
const names = ["Kenny", "Kyle", "Eric", "Butters"]

// find out if ALL the names have an "E" or "e"
const hasE = names.every((name) => name.toLowerCase().includes('e'))

console.log(hasE)
// true

// what if we don't use toLowerCase()?
const hasSmallE = names.every((name) => name.includes('e'))

console.log(hasSmallE)
// false

// find out if ANY of the names have a lowercase "e"
const eAnywhere = names.some((name) => name.includes('e'))

console.log(eAnywhere)
// true (Kenny, Kyle and Butters have a small "e")

So, both every() and some():

  • return a simple Boolean indicating
    • a uniformity (in the case of every()) or
    • an existence (in the case of some())
  • the condition in question appears in the body of the callback function
    • the callback function gets passed as the one argument
⬅️ older (in textbook-javascript)
📒 JavaScript arrays
newer (in textbook-javascript) ➡️
JavaScript modules 📒
⬅️ older (in code)
📒 JavaScript arrays
newer (in code) ➡️
JavaScript modules 📒
⬅️ older (posts)
📒 JavaScript arrays
newer (posts) ➡️
JavaScript modules 📒