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.
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 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
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
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
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
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. Themap()
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
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
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
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
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:
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 thereduce()
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.
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.
Top comments (2)
Great one
Thanksπ