Higher-Order Functions
Introduction
Functions are the building blocks of JavaScript. By defining a block of code and executing it when we choose, we can build complex applications whilst keeping our code clean, easy to understand, and reusable.
In their simplest form, functions in JavaScript are declared and invoked as below:
function multiply(a, b) {
return a * b;
}
multiply(2, 3); // 6
These are referred to as first-order functions and are one of the first things we learn how to do in JavaScript.
What is a Higher-Order Function?
A higher-order function is a function that takes at least one or more functions as an argument, or returns a function. Higher-order functions help us abstract logic, making code more readable and reusable.
Two examples of higher-order functions in JavaScript are filter
and reduce
. Let’s see how we can use them to solve a problem.
Defining a Problem
You are given the following data:
'1 2 543543 a word word 34 &^string 456 324'
You are asked to provide the sum total of all the numbers in that (questionable) sentence.
Here’s how you might approach this problem using just first-order functions:
function getNumbers(sentence) {
const string = sentence.split(" ");
const filtered = [];
for (let i = 0, { length } = string; i < length; i++) {
const word = string[i];
if (!isNaN(word)) filtered.push(parseInt(word));
}
return filtered;
}
function sumArray(arr) {
let sum = 0;
for (var i in arr) {
sum += arr[i];
}
return sum;
}
console.log(sumArray(getNumbers("1 2 543543 a word word 34 &^string 456 324"))); // 544360
… a fairly convoluted approach and full of mistakes, but hopefully you can see the point I’m trying to make.
Using higher-order functions, we can abstract a lot of this logic and make it more readable and more reusable.
Using Higher-Order Functions: filter and reduce
filter
const sentence = "1 2 543543 a word word 34 &^string 456 324";
const result = sentence.split(" ").filter(word => !isNaN(word));
console.log(result); // ["1","2","543543","34","456","324"]
filter
is a higher-order function that takes a function as an argument. It then returns a new array dependent on the logic inside that function, in this case !isNaN(word)
which is checking if the word given is actually a number or not.
reduce
Let’s now see if we can take that array and sum it using another higher-order function, reduce:
let total = ["1", "2", "543543", "34", "456", "324"].reduce(
(previousValue, currentValue) =>
parseInt(previousValue) + parseInt(currentValue),
0 // initial value
);
console.log(total); // 544360
reduce takes a function that performs some logic on each element of an array, but can also access the previous element of an array, making it a perfect candidate for summing an array.
Chained together, the previous example can now look like this:
let getTotalFromSentence = sentence =>
sentence
.split(" ")
.filter(word => !isNaN(word))
.reduce(
(previousValue, currentValue) =>
parseInt(previousValue) + parseInt(currentValue),
0
);
console.log(getTotalFromSentence("1 2 543543 a word word 34 &^string 456 324")); // 544360
The example is definitely leaner, and no doubt could be improved further (looking at you, parseInt) - but hopefully, I’ve highlighted how higher-order functions can make code easier to read, produce, and reuse.
Further reading
There are plenty of higher-order functions that come as standard now in JS, but to start off here are 3 that I use quite a bit:
- Array.map() - MDN
- Array.filter() - MDN
- Array.reduce() - MDN
Happy coding!