Higher-Order Functions In Javascript
Tags: javascript, codenewbie, functional, 100daysofcode
Published: 2021-01-24 17:02:48

Hello Developers!!👩‍💻👨‍💻 In this post, we'll learn about the important concept which makes Javascript suitable for functional programming, **Higher-Order Functions**. If you are a Javascript developer then you may have already used them without even knowing.

To fully understand the *Higher-Order Functions*, we first need to understand the concept of [First Class Functions](https://dev.to/anuradha9712/first-class-functions-in-javascript-45c5).

> A programming language is said to have First-class functions when functions in that language are treated like any other variable. For example, in such a language, a function can be passed as an argument to other functions, can be returned by another function, and can be assigned as a value to a variable.

If you are not aware of what [First Class Functions](https://dev.to/anuradha9712/first-class-functions-in-javascript-45c5) is so I highly recommend you to read the previous article in this series about this topic for clear understanding.

In this article, we'll cover the following concepts in detail:

- What is Higher-Order Functions
- built-in Higher-Order Methods
- Filtering arrays
- Transforming with map
- Summarizing with reduce
- Composability

Now, let's start our journey 🚀

## What is Higher-Order Functions 🧐?

> Functions that operate on other functions, either by taking them as arguments or by returning them, are called higher-order functions. 
Higher-order functions allow us to abstract over *actions*, not just values. 

Let's try to understand this using an example:

**Example 1: Functions that operate on other functions by returning them**

```javascript
function outer(a, b){
    return function inner(){
        return a * b;
    }
}

``` 
In the above example, the `outer()` function acts as a **Higher-Order Functions** by returning an another function `inner()`.

**Example 2: Functions that operate on other functions by taking them as arguments**

```javascript
function multiplyFn(a,b){
    return a * b;
}

// Higher Order Function
function displayResult(multiply){
    for(let x = 1; x <= 10; x++){
        console.log(multiply(x,x));
    }
}

displayResult(multiplyFn);
``` 

In the above code snippet, `displayResult` is a Higher-Order function that takes a `multiplyFn` as an argument and operates on them. Taking another function as an argument is often referred to as a **callback function** because it is called back by the higher-order function.

## Built-in Higher-Order Methods 🤓

Arrays provide a number of useful higher-order methods. 
- You can use the `filter` method to returns a new array containing only the elements that pass the predicate function. 
- Transforming an array by putting each element through a function is done with a `map`. 
- You can use `reduce` to combine all the elements in an array into a single value. 
- The `some` method tests whether any element matches a given predicate function. 
- `findIndex` finds the position of the first element that matches a predicate.

These are some of the examples of *Higher-Order Functions*.

Higher-order functions allow you to write simpler and more elegant code.

Let’s take a look at some examples of built-in higher-order functions and see how it compares to solutions where we aren’t using Higher-Order Functions.

### Filtering arrays 📋 

> The `filter()` method creates a new array with all elements that pass the test implemented by the provided function.

**Example 1: `filter()` Without Higher-Order Functions**

```javascript
let marks = [100, 200, 300, 400, 500];

let result = [];

for (let x = 0; x < marks.length; x++){
    if(marks[x] > 300){
        result.push(marks[x]);
    }
}

console.log(result); // [400, 500]
``` 

**Example 2: `filter()` With Higher-Order Functions**

```javascript
let marks = [100, 200, 300, 400, 500];

let ans = marks.filter((item)=> item > 300)

console.log(ans); // [400, 500]

console.log(marks); // [100, 200, 300, 400, 500]
``` 
Note how the filter function, rather than deleting elements from the existing array, builds up a new array with only the elements that pass the test. This function is **pure**. It does not modify the array it is given.

### Transforming with map 🔍

> The `map()` method creates a new array by calling the callback function provided as an argument on every element in the input array. The `map()` method will take every returned value from the callback function and creates a new array using those values.

**Example 1: `map()` Without Higher-Order Functions**


```javascript
let marks = [100, 200, 300, 400, 500];

let result = [];

for (let x = 0; x < marks.length; x++) {
    result.push(marks[x] * 2);
}

console.log(result); // [200, 400, 600, 800, 1000]
``` 

**Example 2: `map()` With Higher-Order Functions**


```javascript
let marks = [100, 200, 300, 400, 500];

let result = marks.map((item) => item * 2);

console.log(result); // [200, 400, 600, 800, 1000]
``` 

The `map` method transforms an array by applying a function to all of its elements and building a new array from the returned values. The new array will have the same length as the input array, but its content will have been mapped to a new form by the function.

### Summarizing with reduce 👩‍🏫 

> The `reduce()` method executes a reducer function (that you provide) on each element of the array, resulting in a single output value.

**Example 1: `reduce()` Without Higher-Order Functions**


```javascript
let marks = [100, 200, 300, 400, 500];

let sum = 0;

for(let i=0; i< marks.length; i++){
    sum += marks[i];
}

console.log(sum); // 1500
``` 

**Example 2: `reduce()` With Higher-Order Function**


```javascript
let marks = [100, 200, 300, 400, 500];

const reducer = (accumulator, currentValue) => accumulator + currentValue;

console.log(marks.reduce(reducer)); // 1500
``` 

Your reducer function's returned value is assigned to the accumulator, whose value is remembered across each iteration throughout the array, and ultimately becomes the final, single resulting value.

💡💡The above code snippets show the advantages of the Higher-Order Function which made our code cleaner, more concise, and less verbose.

## Composability

> Function composition is the process of combining two or more functions to produce a new function. Composing functions together is like snapping together a series of pipes for our data to flow through.

Higher-order functions start to shine when you need to **compose** operations.

Take a look at the following example:


```javascript
let marks = [123, 456, 789, 3201, 6054, 987];

let reducer = (accumulator, currentValue) => accumulator + currentValue;

let average = grades => (
    grades.reduce(reducer) / grades.length
)

console.log(Math.round(average(marks.filter((item) => item > 500))));   // 2758
``` 

In the above code snippet: 
- Given an array of marks, we need to find the average of all the marks greater than 500.
- First, we apply a `filter()` method on marks array which gives us a new array containing marks greater than 500.
- Then we pass the above resultant array (containing marks greater than 500) to the `average()` method.
- `average()` method in turn uses the `reduce()` method to find out the sum of all the elements in an array and divides it with the total array's length to find out the *average* of marks.
- At last, we pass the above resultant number to `Math.round()`  which yields our final result, i.e. 2758.

🤯🤯 That's too much work with a single line of code!! 

 If you are confused 😵😵 with the above example then take a look at the below code for a clear understanding.


```javascript
let marks = [123, 456, 789, 3201, 6054, 987];

let reducer = (accumulator, currentValue) => accumulator + currentValue;

let average = grades => (
    grades.reduce(reducer) / grades.length
)

let marksGreaterThan500 = marks.filter((item) => item > 500);
console.log(marksGreaterThan500); // [789, 3201, 6054, 987]

let averageMarks = average(marksGreaterThan500);
console.log(averageMarks); // 2757.75

let roundOffResult = Math.round(averageMarks);
console.log(roundOffResult); // 2758
``` 

I hope now you understand the power of Higher-Order Functions. This helps us to write down complex functionality with clean code that is easy to read, understand and debug.

Being able to pass function values to other functions is a deeply useful aspect of JavaScript. It allows us to write functions that model computations with “gaps” in them. The code that calls these functions can fill in the gaps by providing function values.

## Wrap Up !!

Thank you for your time !! Let's connect to learn and grow together.

[LinkedIn](https://www.linkedin.com/in/anuradha-aggarwal-4a2751107/) [Twitter](https://twitter.com/Anuradh06359394)

[![Buy-me-a-coffee](https://cdn.buymeacoffee.com/buttons/v2/default-blue.png)](https://www.buymeacoffee.com/anuradha2612)