DEV Community

Damien Cosset
Damien Cosset

Posted on

Higher Order Functions JavaScript Higher-order functions in Javascript

Introduction

In Javascript, functions are values ( first-class citizens ). This means that they can be assigned to a variable and/or passed as a value.

let random = function(){
    return Math.random()
}

let giveMeRandom = random // assigning random to a variable
Enter fullscreen mode Exit fullscreen mode

This single piece of knowledge allows us to write functional programming in this language. In functional programming, we heavily use higher-order functions.

Higher-order functions?

Higher-order functions are functions that take other functions as arguments or return functions as their results.

Taking an other function as an argument is often referred as a callback function, because it is called back by the higher-order function. This is a concept that Javascript uses a lot.

For example, the map function on arrays is a higher order function. The map function takes a function as an argument.


const double = n => n * 2

[1, 2, 3, 4].map(double) // [ 2, 4, 6, 8 ]
Enter fullscreen mode Exit fullscreen mode

Or, with an anonymous function:

[1, 2, 3, 4].map(function(n){
    return n * 2
}) // [ 2, 4, 6, 8 ]
Enter fullscreen mode Exit fullscreen mode

The map function is one of the many higher-order functions built into the language. sort, reduce, filter, forEach are other examples of higher-order functions built into the language.

Higher-order functions allows you to write simpler and more elegant code. Let's look at what the code above would look like without such an abstraction. Let's replace the map function by a loop:

let array = [1, 2, 3, 4]
let newArray = []

for(let i = 0; n < array.length; i++) {
    newArray[i] = array[i] * 2
}

newArray // [ 2, 4, 6, 8 ]
Enter fullscreen mode Exit fullscreen mode

The power of composition

One of the great advantages of using higher order functions when we can is composition.

We can create smaller functions that only take care of one piece of logic. Then, we compose more complex functions by using different smaller functions.

This technique reduces bugs and makes our code easier to read and understand.

By learning to use higher-order functions, you can start writing better code.

Example

Lets try with an example. Assume we have a list of grades from a classroom. Our classroom has 5 girls, 5 boys and each of them has a grade between 0 and 20.

var grades = [
    {name: 'John', grade: 8, sex: 'M'},
    {name: 'Sarah', grade: 12, sex: 'F'},
    {name: 'Bob', grade: 16, sex: 'M'},
    {name: 'Johnny', grade: 2, sex: 'M'},
    {name: 'Ethan', grade: 4, sex: 'M'},
    {name: 'Paula', grade: 18, sex: 'F'},
    {name: 'Donald', grade: 5, sex: 'M'},
    {name: 'Jennifer', grade: 13, sex: 'F'},
    {name: 'Courtney', grade: 15, sex: 'F'},
    {name: 'Jane', grade: 9, sex: 'F'}
]
Enter fullscreen mode Exit fullscreen mode

I want to know a few things about this:

  • The average grade of this classroom
  • The average grade of the boys
  • The average grade of the girls
  • The higher note among the boys
  • The higher note among the girls

We will try to use higher-order functions to get a program that is simple and easy to read. Let's start by writing simple functions that can work together:

let isBoy = student => student.sex === 'M'

let isGirl = student => student.sex === 'F'

let getBoys = grades => (
    grades.filter(isBoy)
)

let getGirls = grades => (
    grades.filter(isGirl)
)

let average = grades => (
    grades.reduce((acc, curr) => (
        acc + curr.grade
    ), 0) / grades.length
)

let maxGrade = grades => (
    Math.max(...grades.map(student => student.grade))
)

let minGrade = grades => (
    Math.min(...grades.map(student => student.grade))
)

Enter fullscreen mode Exit fullscreen mode

I wrote 7 functions, and each of them has one job, and one job only.

isBoy and isGirl are responsible for checking if one student is a boy or a girl.

getBoys and getGirls are responsible for getting all the boys or girls from the classroom.

maxGrade and minGrade are responsible for getting the greatest and lowest grade in some data.

Finally, average is responsible to calculate the average grade of some data.

Notice that the average function doesn't know anything about the type of data it's suppose to process yet. That's the beauty of composition. We can re-use our code in different places. I can just plug this function with others.

Now, we have what we need to write higher-order functions:


let classroomAverage = average(grades) // 10.2
let boysAverage = average(getBoys(grades)) // 7
let girlsAverage = average(getGirls(grades)) // 13.4
let highestGrade = maxGrade(grades) // 18
let lowestGrade = minGrade(grades) // 2
let highestBoysGrade = maxGrade(getBoys(grades)) // 16
let lowestBoysGrade = minGrade(getBoys(grades)) // 2
let highestGirlsGrade = maxGrade(getGirls(grades)) // 18
let lowestGirlsGrade = minGrade(getGirls(grades)) // 9
Enter fullscreen mode Exit fullscreen mode

Notice that the outer functions, average for example, always take as an input the output from the inner functions. Therefore, the only condition to composition is to make sure that the output and input match.

And because each function is responsible for only one thing, it makes our code that much easier to debug and to test.

Composition rules!

Top comments (17)

Collapse
 
aditya81070 profile image
Aditya Agarwal

You should also tell about the closures when talking about the higher order functions. This is the concept most of the javascript developer use but never heard of it.

Collapse
 
damcosset profile image
Damien Cosset

I am working on a separate article about closures ;)

I thought it would be too long of an article if I tried to explain both things in the same post

Collapse
 
captrespect profile image
Jonathon Roberts

Wow, Thanks for the nice simple explanation. Turns out I've been using this for years without giving it a name. Also been reading the term HoF for a while without bothering to lookup what it really was.

Collapse
 
jsonpoindexter profile image
Jason Poindexter • Edited

Excellent post. This helped a lot. I was wondering, what does the zero do here?:

let average = grades => (
    grades.reduce((acc, curr) => (
        acc + curr.grade
    ), 0) / grades.length
)
Enter fullscreen mode Exit fullscreen mode
Collapse
 
damcosset profile image
Damien Cosset

The reduce function takes an optional second parameter that indicates the initial value. If I wanted to start counting from 10,that second parameter would have been 10. Here, I wanted to start counting from 0.

Collapse
 
renatolazaroch profile image
Renato Lazaro • Edited

I believe you have set the variable n by mistake inside the for.

let array = [1, 2, 3, 4];
let newArray = [];
for (let i = 0; i < array.length; i++) {
newArray[i] = array[i] * 2;
}
console.log(newArray);
// 2, 4, 6, 8

=============================
High Order Functions

let numbers = [1, 2, 3, 4];
const result = numbers.map((n) => n * 2);
console.log(result);
// 2, 4, 6, 8

Collapse
 
renatolazaroch profile image
Renato Lazaro

I believe you have set the variable n by mistake inside the for.

let array = [1, 2, 3, 4];
let newArray = [];
for (let i = 0; i < array.length; i++) {
newArray[i] = array[i] * 2;
}
console.log(newArray);
// 2, 4, 6, 8

=============================
High Order Functions

let numbers = [1, 2, 3, 4];
const result = numbers.map((n) => n * 2);
console.log(result);
// 2, 4, 6, 8

Collapse
 
yuriifediv profile image
Yurii Fediv • Edited

Higher-order functions are functions that take other functions as arguments or return functions as their results.
One of the great advantages of using higher order functions when we can is composition.

Sorry, but higher order functions and your example(I mean your example with grades, not first part with map) aren't linked at all. None of the functions from your example takes other functions as arguments or returns function as a result.
Of course, you use functions inside other functions and you pass results of other functions as arguments to other functions, but it is not a higher order function if we look at your definition above.

Collapse
 
dumdumme profile image
Shreenath

Good catch.

Collapse
 
renatolazaroch profile image
Renato Lazaro

I believe you have set the variable n by mistake inside the for.

let array = [1, 2, 3, 4];
let newArray = [];

for (let i = 0; i < array.length; i++) {
newArray[i] = array[i] * 2;
}

console.log(newArray);

Collapse
 
kumarvinoth123 profile image
kumarvinoth123

wow really awesome..this i learn clearly....

Collapse
 
johnson76096186 profile image
Johnson Jay

1.
Save a variable, time2p2. Assign as its value the result of invoking the timeFuncRuntime() function with the checkThatTwoPlusTwoEqualsFourAMillionTimes() function.

timeFuncRuntime() takes a function as an argument. The argument for timeFuncRuntime() is a callback function: timeFuncRuntime() has to actually call the callback function to see how long it takes to execute. Pass the checkThatTwoPlusTwoEqualsFourAMillionTimes() function as the argument for the timeFuncRuntime() function. Make sure to pass in the function without invoking it... What next to do?

Collapse
 
nazg00l profile image
Nazg00l

Thank you so much, you really made it simple, easy, and full of great explanation and examples.....