WP Latest

Higher Order Functions in JavaScript

higher_order_functions.png

Higher Order Functions in JavaScript

A higher order function is a function that takes another function as a parameter, or returns a function as the output. Higher-order functions are widely used and implemented in JavaScript to improve code readability and flexibility. Integrating higher-order functions in your code logic avoids code redundancy, saving time and resources. 

This tutorial will introduce you to higher-order functions, how to use them, and their benefits.

How to Pass a Function as an Argument to Another function.

Passing functions as arguments to other functions is one of the ways to customize the behavior of functions. It allows you to pass different functions as parameters to a single function, making your code cleaner and reusable. Let’s look at how we can do that.

Example 1

Consider the calculate() function, which helps us perform different mathematical calculations. It takes two numbers, a and b, and an operation function as arguments. We then define the different mathematical functions, i.e., add, and  subtract.

We then use the calculate() function with any of the mathematical functions and provide the integers on which the mathematical operations will be performed.

1
function calculate(a, b, operation) {
2
    return operation(a,b);
3
  }
4
  
5
 
6
  function add(a, b) {
7
    return a + b;
8
  }
9
  
10
  function subtract(a, b) {
11
    return a - b;
12
  }
13
  
14

15
console.log(calculate(10, 5, add)); //output // 15
16
console.log(calculate(10, 5, subtract)); //output //5

As you can see above, by separating the different operations, our code can be easily extensible without changing the calculate () function.

Example 2

Let’s look at another example. Suppose you have the array of names shown below;

1
const names = ['Ann','Alex', 'Barman', 'zen','liz', 'Christopher', 'Isabella']

Then suppose you were tasked with filtering out the people whose names have three characters or less; you would probably come up with something like this:

1
function shortNames(namesArr){
2
    const result = []
3
    for(i=0; i< namesArr.length; i++){
4
        if (namesArr[i].length <= 3){
5
            result.push(namesArr[i])
6
            
7
        }
8
    }
9
    return result
10
}
11

12
console.log(shortNames(names)) //output //[ 'Ann', 'zen', 'Liz' ]

Here, we create a function shortNames, which takes an array of names as a parameter. Then, we defined an empty array called result. Then, we create a for loop, which loops through each element in the array and checks its length. If the length of an element is less than or equal to 3 characters, the element is considered a short name and pushed to the result array. Finally, we return the new array containing the filtered short names.

Suppose we also need to get all the people with long names, i.e., names with eight or more characters; our function would look similar to the shortNames() function

1
function LongNames(namesArr){
2
    const result = []
3
    for(i=0; i< namesArr.length; i++){
4
        if (namesArr[i].length >= 8){
5
            result.push(namesArr[i]) 
6
            
7
        }
8
    }
9
    return result
10
}
11
console.log(LongNames(names)); //ouput // [ 'Christopher', 'Isabella' ]

The LongNames and shortNames functions both perform similar tasks, such as:

  • looping through the names array
  • filtering each name in the array based on the specified condition
  • pushing elements that satisfy the condition to a new array

However, we can shorten our code and avoid repetitive tasks by creating a common function. Since we don’t need all the logic in our two functions above, we can rewrite them as shown below.

1
function isShortName(name, length) {
2
    return name.length <= length;
3
}
4

5
function isLongName(name, length) {
6
    return name.length >= length;
7
}

Here, we define our functions isShortName and isLongName, which take two arguments: name and length. isShortName will check if the given name is less than or equal to the specified length. It returns true if the given name satisfies the condition. 

IsLongName() does something similar but returns true if the given name is more than or equal to the provided length.

Next, we will create a filterNames function to filter names based on different conditions. The filterNames function will take three arguments:

  • names array.
  • The callback function(can either be  IsLongName or isShortName).
  • The length condition used to filter the names.
1
function filterNames(namesArr, conditionFn, length) {
2
    const result = [];
3
    for (let i = 0; i < namesArr.length; i++) {
4
        if (conditionFn(namesArr[i], length)) {
5
            result.push(namesArr[i]);
6
        }
7
    }
8
    return result;
9
}

So now if we decide to use the filterNames()  with any of the callback functions, we will get the same output.

1
console.log(filterNames(names, isShortName, 3)); // [ 'Ann', 'zen', 'Liz' ]
2
console.log(filterNames(names, isLongName, 8));  //[ 'Christopher', 'Isabella' ]

Examples of Higher Order Functions

Higher-order functions are commonly used for mapping, filtering, and reducing arrays. The most commonly used higher-order functions include:

  • filter() 
  • map()
  • reduce()

Using the filter() method

As the name suggests, the filter() method filters elements on an array based on the specified condition. When applied to an array, the filter() method will create another array with only the elements from the original array that satisfy the condition in the function.

Consider the array below, which consists of the names and salaries of some employees.

1
const employees = [
2
    {name: "Alice",salary: 25000 },
3
    {name: "Bob",salary: 30000},
4
    {name: "Charlie",salary: 28000},
5
    {name: "Cate",salary: 100000,},
6
    {name: "Mike",salary: 120000,},
7
    {name: "Lucy",salary: 55000,},
8
    {name: "Liam",salary: 70000,},
9
]

Suppose we want to filter out the employees earning more than 70,000. One way to do this would be using a for loop, loop over each element and, with each iteration, push the employee that satisfies our condition to a new array, as shown below.

1
const filteredEmployees = []
2
for(i =0 ;i <employees.length; i++){
3
    if(employees[i].salary >=70000 ){
4
        filteredEmployees.push(employees[i])
5
 }}
6
 
7
 console.log(filteredEmployees);

Even though the function works as expected, there are better ways to implement the solution. The filter () method is an excellent solution to our problem. Its syntax looks like this;

1
const newArray = array.filter(callbackFn,thisArg)

Where callbackFn is the function for filtering elements. The callbackFn takes three optional arguments: element, index, and array.  thisArg is optional.

Let’s first define the callbackFn, which will take in an employee object and check if the value in the salary property is more than 70,000.

1
function checkSalary(employee){
2
    return employee.salary >= 70000
3
}

Next, let’s apply the filter() method to our callbackFxn and assign it to the filtredArray.

1
const filteredArray = employees.filter(checkSalary);
2
console.log(filteredArray)

Our output will look like this:

1
[
2
  { name: 'Cate', salary: 100000 },
3
  { name: 'Mike', salary: 120000 },
4
  { name: 'Liam', salary: 70000 }
5
]

Using the Map() Method

The map() method is another higher-order function which creates a new array by applying a callback function to each element on the original array.

The syntax looks like this:

1
const newArray = originalArray.map(callbackFn, thisArg);

where the callbackFn takes in the following parameters, 

  • currentValue – the current element being processed
  • index – the index of the current element being processed
  • array -the original array 

thisArg is optional.

Given the students array below, which contains students’ names and grades for different subjects and overall grades, we want to extract just the names and overall grades from the array.

1
const students = [
2
    {
3
        names: "Alice Bob",Math: 85,Science: 92,History: 78,English: 88,Art: 95,
4
        grade:87.6
5
    },
6
    {
7
        names: "Michael Smith",Math: 76,Science: 89,History: 92,English: 80,
8
        Art: 91,
9
        grade:85.6
10
    },
11
    {
12
        names: "William Brown",Math: 70,Science: 78,History: 75,English: 88,Art: 79,
13
        grade:76
14
    },
15
    {
16
        names: "Julia Lee", Math: 52, Science: 63, History: 76, English: 87,
17
        Art: 94,
18
        grade:74.2
19
    },
20
    {
21
        names:"George Harrison",Math: 88,Science: 77,History: 50,English: 84,
22
        Art: 71,
23
        grade:74
24
    },
25
];

We can use the map() method to retrieve the student names and overall grades. First, let’s create the callback function, which takes a student as an argument and extracts the student’s name and the overall grade.

1
function gradeOnly(student){
2
   return ({names:student.names, grade: student.grade})
3
}

Our callback function will take a student object as an argument and return a new object containing only the names and grade properties. 

Next, we will use the map() method to create a new array by applying the gradeOnly() to each student in the students array.  The map() method will iterate through each student array element and apply the gradeOnly() function. 

1
const studentsData = students.map(gradeOnly)
2
console.log(studentsData)

Our output will be:

1
[
2
  { names: 'Alice Bob', grade: 87.6 },
3
  { names: 'Michael Smith', grade: 85.6 },
4
  { names: 'William Brown', grade: 76 },
5
  { names: 'Julia Lee', grade: 74.2 },
6
  { names: 'George Harrison', grade: 74 }
7
]

Using arrow functions, we can simplify this expression as follows:

1
const studentsData = students.map(
2
    student=> ({names:student.names, grade: student.grade}))
3
console.log(studentsData)

Using Array.reduce() Method

As the name suggests, the reduce() method takes in an array and reduces it to a single value. The syntax for the reduce method looks like this.

1
array.reduce(function(total, currentValue, currentIndex, arr), initialValue)

where

  • The total will be the result
  • the currentValue will be the current element during the iteration process
  • The initialValue will be 0
  • currentIndex and arr are optional 

Suppose you wanted to find out the sum of numbers in the numbers array below using the reduce() method:

1
numbers = [10,20,30,40]

Let’s start by defining the callback function that will take in the total and number as arguments. The callback function will return the result of adding each number in our original array to the total. 

1
function addNumbers(total,number){
2
    return total+=number
3
}

Next, apply the reduce() method to the numbers array and use the addNumbers as the callback function. Each element in the numbers array is applied to the callback function for each iteration.

1
const cumulativeTotal =numbers.reduce(addNumbers);
2
console.log(cumulativeTotal); //output //100

The output will be 100, as expected.  If we don’t define an initial value, the first element will be considered the initial value, and the output will still be the same.

1
numbers = [10,20,30,40]
2
numbers.reduce(function(total, number){
3
    return total+number
4
})

We can further shorten our expression using arrow functions as follows:

1
const cumulativeTotal = numbers.reduce((total,number) => total+=number)
2
console.log(cumulativeTotal); //output //100

Benefits of Higher Order Functions

  • Higher-order functions abstract the underlying logic required for performing tasks. For example, when we used the reduce() method, we abstracted the underlying logic of iterating through each element, adding it to the total, and returning the result. We didn’t need to write the iteration and accumulation logic explicitly; the reduce function handled it for us.
  • Utilizing higher-order functions enhances code readability and maintainability compared to implementing the same functionality with loops.
  • Higher-order functions also make your code reusable.

Up Your JS Game With Tuts+

Conclusion

This tutorial introduced you to higher-order functions and offered practical examples and scenarios where they can be applied to JavaScript programming. Incorporating higher-order functions into your development practices can significantly enhance your skills as a JavaScript developer.

Leave your thought here

Your email address will not be published. Required fields are marked *

Enable Notifications OK No thanks