Let’s Talk about JavaScript’s Higher-Order Functions — Pt. 1
Taking a look at
As a Bootcamp graduate, I still find some programming vocabulary pretty intimidating and constantly fear using the wrong term or phrase when describing code. This is the hardest part: understanding the core concepts.
“Higher-order functions” is one of the phrases I first struggled with at first. In 3 Ways to Write Cleaner Code, I wrote about the DRY principle, or Don’t Repeat Yourself. In functional programming, one of the ways to accomplish this is by using higher-order functions, which take a function as an argument, return a function, or both. It is simply a decorative phrase for a simple concept.
As a quick review, remember that JavaScript functions are objects that can be
- passed as arguments (which are called callback functions)
- stored as variables
- returned from other functions
- used in arrays
- assigned as object properties (which are just methods, a function inside an object)
These characteristics are true for any other type of data as well! So what’s the big deal about them? Think back on a function you’ve written that is more than 6 lines. Higher-order functions allow us to abstract logic from our functions and create helper functions that focus on smaller tasks.
They also help our code become more declarative: short, simpler, and readable.
In this post, we’ll be looking at the commonly used higher-order functions–Array.prototype methods: .filter()
and .map()
as well as their alternative for
loop solution.
.filter()
Definition + Syntax — MDN
/*
Definition:
The filter() method creates a new array with all elements that pass the test implemented by the provided function.
*/// Syntax:
let newArray = arr.filter(callback(currentValue[, index[, array]]) {
// return element for newArray, if true
}[, thisArg]);
When to Use:
Sometimes we only need certain values in array. We can apply .filter()
to return a new array that contains that subset. This method is a great choice because it doesn’t mutate the original array.
In Action:
From the Codewars problem, Vowel Count –
Return the number (count) of vowels in the given string.
We will consider a, e, i, o, u as vowels for this Kata (but not y).
The input string will only consist of lower case letters and/or spaces.
Input: "abracadabra"
Expected Output: 5const vowels = ['a', 'e', 'i', 'o', 'u']const getVowelCount = (word) => {
let vowelsCount = 0; let splitWord = Array.from(word)
splitWord.filter(letter => {
vowels.includes(letter)
? vowelsCount++
: null
});
return vowelsCount;
}
First, we declared the variable vowels
that holds an array of all valid vowels. Next, we define our function getVowelCount
the ES2015 (ES6) way. Inside getVowelCount
, we declare our counter variable vowelCount
and splitWord
, which splits the given string input into an array of its characters.
Now, we’ll use .filter
to compare each of the letters in our splitWord
array to see if it matches any of the valid vowels in our vowels
array. In this function, we are saying: in splitWord
, let’s look at each letter and see if it matches (or is included) in the vowels
array. If it is included, let’s increment our counter, if it isn’t a vowel, do nothing. Then we return the vowelCount
once that is finished!
Let’s compare it to the for
loop solution:
const getVowelCount = (word) => {
let vowelCount = 0;
let vowels = ['a', 'e', 'i', 'o', 'u']; for (let i = 0; i < word.length; i++) {
for (let j = 0; j < vowels.length; j++) {
if (word[i] === vowels[j]) {
vowelCount++;
}
}
}
return vowelCount;
}
With filter
, the solution becomes a bit more readable and helps us avoid writing nested for
loops.
.map()
Definition + Syntax — MDN
/*
Definition:
The map() method creates a new array populated with the results of calling a provided function on every element in the calling array.
*/// Syntax:
let newArray = arr.map(callback(currentValue[, index[, array]]) {
// return element for newArray, after executing something
}[, thisArg]);
When to Use:
While .forEach()
could be used to achieve the same thing, it’s better to use .map()
instead to implement a high-order function that doesn’t mutate the original array. Use .map()
when you need to do something to each element inside of an array and need a new array with those values. We’ll pass .map()
a function to execute on each element in order to receive those values.
In Action:
Let’s say you need an array of every letter of the alphabet and lowercase. Instead of hardcoding all 26 letters, you could write a function using the staticString.fromCharCode()
method which returns a string created from a sequence of specified UTF-16 code units. In this example, we’ll be using the Decimal value of the corresponding ASCII character in the alphabet. You can find a table of those values here.
const alphabet = [...new Array(26).keys()]
.map(i => String.fromCharCode(i + 97))
First, we create a new array holding the numerical values of their corresponding index using the spread operator, the Array constructor, and the keys
method. Let’s break down each return value:
[...new Array(26)] =>
[
undefined, undefined, undefined,
undefined, undefined, undefined,
undefined, undefined, undefined,
undefined, undefined, undefined,
undefined, undefined, undefined,
undefined, undefined, undefined,
undefined, undefined, undefined,
undefined, undefined, undefined,
undefined, undefined
][...new Array(26).keys()] =>
[
0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25
].map(i => String.fromCharCode(i + 97)) =>
[
'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r',
's', 't', 'u', 'v', 'w', 'x',
'y', 'z'
]
Knowing that the decimal value of the lowercase letter a starts at 97, we can now map over this new array, passingString.fromCharCode
each number in our array plus 97 to get every letter in the alphabet.
Let’s compare a for
loop solution:
function setAlphaIndex() {
let alphaIndex = [];
for (let i = 0; i < 26; i++) {
alphaIndex.push(i)
}
return alphaIndex;
}function getLowecaseAlphabet() {
let alphabet = [];
let alphaIncrements = setAlphaIndex();
for (let i = 0; i < alphaIncrements.length; i++) {
let letter = String.fromCharCode(i + 97)
alphabet.push(letter)
}
return alphabet
}
Instead of creating one giant function, I separated the logic for creating a new array with corresponding indices as its values. Now, I’m calling my helper function inside getLowerCaseAlphabet()
and setting it to a variable. It’s definitely not the cleanest of code, but I wanted to illustrate what is happening under the hood of the .map
solution!
Codewars is a great place to practice switching between writing with both for
loops and JavaScript’s built-in methods. In the next post, we’ll take a look at .reduce()
and .includes()
and how they can be utilized in a HackerRank problem!