DEV Community

Cover image for ES6 - Higher-Order Functions
skaytech
skaytech

Posted on • Updated on • Originally published at blog.skay.dev

ES6 - Higher-Order Functions

Introduction

In this article, we will first understand what a higher-order function is and then look at a few code examples of the popular higher-order functions such as forEach, map, filter, reduce, etc.

What is Higher Order Functions?

It is important to first understand what first-class citizens are before we move to higher-order functions.

First-Class Citizens

In JavaScript, functions are treated as 'First Class' citizens. The reason is that in JavaScript a function is fundamentally an object.

Let us take a quick example to explain this better. The below code is an example of a simple function.

//A simple function
function sayHello() {
    console.log('Hello');
}

//Invoke the function
sayHello(); //output -> Hello
Enter fullscreen mode Exit fullscreen mode

When we say that Functions are objects, we can assign properties to the function similar to an object as shown below.

//Since the function is essentially an object, you can add properties to it
sayHello.greet = 'Namaste';

console.log(sayHello.greet); // Output -> Namaste
Enter fullscreen mode Exit fullscreen mode

Note: While we can assign an attribute to a function. It is generally considered a bad practice. If you must add an attribute, then ensure you use an object.

You can also assign variables to a function as the code example shown below:

//A simple function
const sayHello = function() {
    console.log('Hello');
}

//Invoke the function
sayHello(); //output -> Hello
Enter fullscreen mode Exit fullscreen mode

The above examples are that of 'First-Class' functions.

Higher-Order Functions

Higher-order functions are functions that take other functions as arguments or functions that return a function as their result.

The below code example will hopefully make the above explanation more clear.

//Function Sum of two arguments a & b
const sum = function (a, b) {
  return a + b;
};

//Higher order function - takes 'func' as an argument & returns a 'func' for execution
const higherOrderFunction = (func) => (a, b) => {
  return func(a, b);
};
const sumFor = higherOrderFunction(sum);

console.log(sumFor);

/**
 *  The Output of sumFor (HigherOrderFunction) is
 *  (a, b) => {
 *     return func(a, b);
 *  }
 * 
 */

console.log(sumFor(2, 3)); //Output -> 5
Enter fullscreen mode Exit fullscreen mode

Things to note:

  • The function 'higherOrderFunction' accepts a function 'func' as a parameter.
  • The function 'func' that is passed in as a parameter is referred to as a callback.

Array.prototype.forEach, Array.prototype.map, Array.prototype.filter are few examples of high-order functions introduced with ES5.

Let's dive into the actual functions.

Array.forEach

The forEach function was introduced with ES5 as Array.prototype.forEach and with ES6, it's simply referenced as Array.forEach.

Syntax: Array.forEach( callback( currentValue, currentIndex, array));

The callback function receives three parameters:

  • The current element value - Mandatory
  • The current element index - Optional
  • The array that is being traversed.

Let us look at a code example:

//An array of Marvel Movies with the title & year of release
const marvelMovies = [
  {
    title: "Iron Man",
    year: 2008,
  },
  {
    title: "The Avengers",
    year: 2012,
  },
  {
    title: "Doctor Strange",
    year: 2016,
  },
  {
    title: "Avengers: Infinity War",
    year: 2018,
  },
  {
    title: "Avengers: End Game",
    year: 2019,
  },
];

//forEach loops through each movie & prints the name of the title & year it was released on the console
//The anonymous function accepts 'movie' as an argument which matches with the current element
marvelMovies.forEach((movie) => {
  console.log(`The title of the movie is $${movie.title}`);
  console.log(`${movie.year} is the year the movie was released.`);
});

/*
Output
---------
The title of the movie is $Iron Man
2008 is the year the movie was released.
The title of the movie is $The Avengers
2012 is the year the movie was released.
The title of the movie is $Doctor Strange
2016 is the year the movie was released.
The title of the movie is $Avengers: Infinity War
2018 is the year the movie was released.
The title of the movie is $Avengers: End Game
2019 is the year the movie was released.
---------
*/
Enter fullscreen mode Exit fullscreen mode

Things to note:

  • The callback function is an anonymous function. It is used in conjunction with an arrow function. If you would like a refresher on arrow function, you can read over here.
  • I have used 'movie' to reference the current element that is passed in as the argument. In reality, you can use any name to the variable you are referencing the current element.
  • The forEach loops through the 'marvelMovies' array and displays the title and the year of release on the console.

Array.map

The map function was introduced with ES5 as Array.prototype.map and with ES6, it's simply referenced as Array.map.

Syntax: Array.map( callback( currentValue, currentIndex, array));

The callback function receives three parameters:

  • The current element value - Mandatory
  • The current element index - Optional
  • The array that is being traversed.

The important thing to note here is that the 'map' function returns a 'new' array with the results of the callback function applied to every element of the original array, in the same order.

Let us take the same marvelMovies array and use the map function to rate only the movies with the title 'Avengers'.

//An array of Marvel Movies with the title & year of release
const marvelMovies = [
  {
    title: "Iron Man",
    year: 2008,
  },
  {
    title: "The Avengers",
    year: 2012,
  },
  {
    title: "Doctor Strange",
    year: 2016,
  },
  {
    title: "Avengers: Infinity War",
    year: 2018,
  },
  {
    title: "Avengers: End Game",
    year: 2019,
  },
];

//map Function
//marvelMovies.map function will return a new array that'll be assigned to newMarvelMovies
const newMarvelMovies = marvelMovies.map((marvelMovie) => {

        //Check if the title contains 'Avengers'
    if(marvelMovie.title.includes('Avengers')) {
                //Add the 'rating' atrribute to the current element
        marvelMovie['rating'] = 5;
    }
        //the element will be returned as a part of the anonymous function
        //the element will be added as a part of the new array being returned from the map function
    return marvelMovie;
});

//Display the value of the new Array 'newMarvelMovies'
console.log(newMarvelMovies);

/*
Output ->
0: {title: "Iron Man", year: 2008}
1: {title: "The Avengers", year: 2012, rating: 5}
2: {title: "Doctor Strange", year: 2016}
3: {title: "Avengers: Infinity War", year: 2018, rating: 5}
4: {title: "Avengers: End Game", year: 2019, rating: 5}
*/
Enter fullscreen mode Exit fullscreen mode

Things to Note:

  • The 'map' function returns a 'new' array.
  • In this example, I've used 'marvelMovie' name for the current element, just to demonstrate that any name can be used for referencing the current element.
  • The main use-case for a 'map' function is to edit or manipulate the elements of the array.

Array.filter

The filter function was introduced with ES5 as Array.prototype.filter and with ES6, it's simply referenced as an Array.filter.

Syntax: Array.filter( callback( currentValue, currentIndex, array));

The callback function receives three parameters:

  • The current element value - Mandatory
  • The current element index - Optional
  • The array that is being traversed.

The filter function creates a new array with all the elements that pass the condition check specified in the callback function.

Let us look at the following code example:

//An array of Marvel Movies with the title & year of release
const marvelMovies = [
  {
    title: "Iron Man",
    year: 2008,
  },
  {
    title: "The Avengers",
    year: 2012,
  },
  {
    title: "Doctor Strange",
    year: 2016,
  },
  {
    title: "Avengers: Infinity War",
    year: 2018,
  },
  {
    title: "Avengers: End Game",
    year: 2019,
  },
];

//filter Function
//filteredMarvelMovies is a new array
//marvelMovies.filter function will return only the elements who's title does not contain 'Avengers'
const filteredMarvelMovies = marvelMovies.filter((marvelMovie) => {
    return !marvelMovie.title.includes('Avengers');
});

//Display the value of the new Array 'filteredMarvelMovies'
console.log(filteredMarvelMovies);

/*
Output ->
0: {title: "Iron Man", year: 2008}
1: {title: "Doctor Strange", year: 2016}
*/
Enter fullscreen mode Exit fullscreen mode

Things to Note:

  • The 'filter' function returns a 'new' array.
  • The main use-case for a 'filter' function is to extract a specific sub-set of elements of the array matching a specific condition.

Array.some

The 'some' function was introduced with ES5 as Array.prototype.filter and with ES6, it's simply referenced as Array.some.

Syntax: Array.some( callback( currentValue, currentIndex, array));

The callback function receives three parameters:

  • The current element value - Mandatory
  • The current element index - Optional
  • The array that is being traversed.

The 'some' function tests for whether at least one element in the array passes the test condition specified in the callback function. It returns a boolean value.

Let us look at the following code example:

//An array of Marvel Movies with the title & year of release
const marvelMovies = [
  {
    title: "Iron Man",
    year: 2008,
  },
  {
    title: "The Avengers",
    year: 2012,
  },
  {
    title: "Doctor Strange",
    year: 2016,
  },
  {
    title: "Avengers: Infinity War",
    year: 2018,
  },
  {
    title: "Avengers: End Game",
    year: 2019,
  },
];

//some function
//Checks & return a true if any one element title of the marvelMovies array 
//matches with the string 'Thor'
const checkTitle = marvelMovies.some(movie => {
    return movie.title === 'Thor'; 
});

//Display the boolean value returned from the 'some' function
console.log(checkTitle); //output -> false
Enter fullscreen mode Exit fullscreen mode

I believe that the above code with the comments above is self-explanatory. Basically, the 'some' function checks if any one of the values in the array matches the condition (title matching 'Thor') and returns the value 'false', since there's no matching value in the marvelMovies array.

Array.every

The 'every' function was introduced with ES5 as Array.prototype.every and with ES6, it's simply referenced as Array.every.

Syntax: Array.every( callback( currentValue, currentIndex, array));

The callback function receives three parameters:

  • The current element value - Mandatory
  • The current element index - Optional
  • The array that is being traversed.

The 'every' function tests whether all the elements in the array pass the condition specified in the callback function. It returns a boolean value.

Let us look at the code example below:

//An array of Marvel Movies with the title, year of release & rating
const marvelMovies = [
  {
    title: "Iron Man",
    year: 2008,
    rating: 5
  },
  {
    title: "The Avengers",
    year: 2012,
    rating: 5
  },
  {
    title: "Doctor Strange",
    year: 2016,
    rating: 4
  },
  {
    title: "Avengers: Infinity War",
    year: 2018,
    rating: 5
  },
  {
    title: "Avengers: End Game",
    year: 2019,
    rating: 5
  },
];

//every function
//Checks & returns a 'true' if all of elements in the marvelMovies array have a rating above 3
const checkRating = marvelMovies.every(movie => {
    return movie.rating > 3; 
});

//Display the boolean value returned from the 'every' function
console.log(checkRating); //output -> true
Enter fullscreen mode Exit fullscreen mode

Array.find & Array.findIndex

The find & findIndex were introduced with ES5 as Array.prototype.find & Array.prototype.findIndex. With ES6, they can be used as Array.find & Array.findIndex.

The Array.find function returns the first matching element based on the condition specified in the callback function.

The Array.findIndex function returns the index of the first matching element based on the condition specified in the callback function.

The code example below gives a quick example of how to use find & findIndex functions.

//An array of Marvel Movies with the title, year of release & rating
const marvelMovies = [
  {
    title: "Iron Man",
    year: 2008,
    rating: 4
  },
  {
    title: "The Avengers",
    year: 2012,
    rating: 3
  },
  {
    title: "Doctor Strange",
    year: 2016,
    rating: 4
  },
  {
    title: "Avengers: Infinity War",
    year: 2018,
    rating: 5
  },
  {
    title: "Avengers: End Game",
    year: 2019,
    rating: 5
  },
];

//find function
//Checks & returns the first element that matches with the condition 'rating' greater than 5
const found = marvelMovies.find(movie => {
    return movie.rating === 4; 
});

//Display the value of 'found' in the Console
console.log(found); 

//output -> {title: "Avengers: Infinity War", year: 2018, rating: 5}
//Even though the movie 'Avengers: End Game' also contains the rating 5,
//the first matching element is returned

--------------------------------------------------------------------------

//findIndex function
//Checks & returns the first element that matches with the condition 'rating' greater than 5
const foundIndex = marvelMovies.findIndex(movie => {
    return movie.rating === 4; 
});

//Display the value of 'foundIndex' in the Console
console.log(foundIndex); //Output -> 3 (Array index starts from 0)
Enter fullscreen mode Exit fullscreen mode

Array.reduce

The 'reduce' function was introduced with ES5 as Array.prototype.reduce. With ES6, it's simplified as Array.reduce.

Syntax: Array.reduce( callback( accumulator, currentValue, currentIndex, array));

The callback function receives four parameters:

  • The accumulator - Mandatory
  • The current element value - Mandatory
  • The current element index - Optional
  • The array that is being traversed.

The reducer function executes the callback function specified over each element of the array to finally return a single value.

In simplified words, through each iteration, the value returned by the callback function is assigned to the 'accumulator' and ultimately becomes the final single resulting value.

The reduce function is the one that's most often not well understood. Hence, I'll take an example without reducer and then use a reducer to explain the code flow.

//The marvelMovies array contains the title & profit each movie has made
const marvelMovies = [
    {
      title: "Iron Man",
      profit: 100000
    },
    {
      title: "The Avengers",
      profit: 200000
    },
    {
      title: "Doctor Strange",
      profit: 200000
    },
];    

//Initialize the totalProfit variable
let totalProfit = 0;

//Use forEach function to loop through the iteration & add the profit of each movie
//to the totalProfit variable
marvelMovies.forEach(movie => {
    totalProfit += movie.profit;
});

//Display the value of totalProfit on console
console.log(totalProfit);
Enter fullscreen mode Exit fullscreen mode

Let us see how the above code can be simplified using the reduce function.

//The marvelMovies array contains the title & profit each movie has made
const marvelMovies = [
    {
      title: "Iron Man",
      profit: 100000
    },
    {
      title: "The Avengers",
      profit: 200000
    },
    {
      title: "Doctor Strange",
      profit: 200000
    },
];

//Use reduce function to loop through the iteration & add the movie's profit
//to the accumulator & return it to the 'totalProfit' variable
const totalProfit = marvelMovies.reduce((accumulator, movie) => {
    return accumulator + movie.profit;
}, 0);

//Display the value of totalProfit on console
console.log(totalProfit);
Enter fullscreen mode Exit fullscreen mode

Things to note:

  • The 'reduce' function takes in an 'accumulator' variable which holds the return value after every iteration.
  • In the example, the movie's profit is added to the 'accumulator' exactly as how 'total' did in the previous example.
  • The main use case of a reduce function is whenever any computation in an array needs to result in a singular value.

Conclusion

To summarize, we started with understanding what higher-order functions are and then saw a few code examples for functions such as forEach, map, filter, reduce, same & every.

The important thing to remember is that both the functions map & filter return 'new' arrays.

The 'map' function is primarily used to manipulate the values of elements of the array based on a condition, whereas, a filter function is used to generally filter out few elements from the array based on a condition. They are very heavily used in actual real-world applications especially with the React framework.

I hope you enjoyed this article. Don't forget to connect with me on Twitter @skaytech.

If you liked this, you may also enjoy:

Top comments (13)

Collapse
 
pentacular profile image
pentacular

Actually, I don't think that I'd consider this to be simpler or more readable.

const totalProfit = marvelMovies.reduce((accumulator, movie) => {
    return accumulator + movie.profit;
}, 0);

The problem with reduce is that it is a general purpose tool, with boilerplate baggage, so it carries almost no meaning in itself, and needs decoding.

In other words, it carries the wrong level of abstraction for direct use.

On the other hand, this would be simpler.

const sum = (array, accessor) => array.reduce((sum, value) => sum + accessor(value), 0);
// ...
const totalProfit = sum(marvelMovies, movie => movie.profit);
Collapse
 
skaytech profile image
skaytech • Edited

Hi Pentacular,

Thanks for reading the article. I believe I have translated the explanation on how to use the reduce function as closely as possible to the MSDN docs.

Reduce is one function that everyone has varying opinions. Your examples have definitely made the syntax shorter using ES6 shorthand techniques.

In my humble opinion, the term 'Simpler' is very relative. What maybe simple to you might not be to othrrs. Having said that, I always welcome other ways to do the same thing without attaching any tags to it 🙏.

Please continue to share your views and thanks once again for reading my article. Cheers!!

Collapse
 
garretharp profile image
Garret

I personally prefer the way in which the article is written. Sure the word sum may sound nicer for this very specific case of array reduce, but you are unnecessarily making a function that does not need to be there.

Collapse
 
skaytech profile image
skaytech

Like I said, it's a personal preference..

Collapse
 
matveyclark profile image
Matthew Clark

Thanks for this, great article. I think one of the best methods to learn the built in higher order functions is to attempt writing your own implementation of it by overriding the 'prototype' of Array, (.forEach, .map etc.) for example. Maybe even add some other functionality just out of curiosity :)

Collapse
 
skaytech profile image
skaytech

You are welcome 🙂 it's true with ES6 things were nicely wrapped with fluff but within prototype methods lies the true implementation

Collapse
 
mohsenalyafei profile image
Mohsen Alyafei

Thanks. Great article.

Collapse
 
skaytech profile image
skaytech

Thanks for reading 🙏

Collapse
 
mohsenalyafei profile image
Mohsen Alyafei

Indeed. This article made me have some new ideas for optimizing my code. Thanks again.

Thread Thread
 
skaytech profile image
skaytech

Cool.. I think there are some great nice code examples which you'll enjoy in this article as well - dev.to/skaytech/es6-spread-operato...

Collapse
 
hemant profile image
Hemant Joshi • Edited

Super helpful post🎉🎉🎉🎉,

It is a big request from my side please keep posting suck posts daily😳, as a Mern stack Dev these are really helpful.

I used to use .map and forEach but now I see a lot of options in my head and I am clearly familiar with the proper examples😁.

Thank you..

Ps: " I read such productive posts during bed time just to over think on the learning"

Collapse
 
skaytech profile image
skaytech

Thanks Hemant. Daily is difficult. I'll try weekly one post. Glad you are enjoying them.

Collapse
 
hemant profile image
Hemant Joshi

Sure😁..