Introduction
Higher order functions are functions that operate on other functions, either by receiving them as arguments or by returning them. It is a function that accepts a function as a parameter or returns a function as the output.
In this article, I am going to show you the most commonly used such methods when it comes to arrays.
Array.prototype.map
This is one of the simplest functions you are going to use while working with Arrays. It forms a new array by calling the function passed into it as an argument, on each and every element of the Array. It will map
each of the return values of the callback and create a new array.
The callback passed to the map()
function can accept any of the three arguments: item
, index
, array
.
Example 1
Given an array of integers, create a new array in which the double of each integer in the first array is stored, and log it to the console.
Solution:
const givenArray = [477, 8914, 40461, 599148];
const newArray = givenArray.map(n => n * 2);
console.log(newArray); // console: [954, 17828, 80922, 1198296]
Example 2
Given an array of singular nouns, create a new array that stores the plural noun of each of the words in the first array, and log it to the console (assume that the singular nouns can be made plural by adding a 's').
Solution:
const givenArray = [ 'pen', 'book', 'code' ];
const newArray = givenArray.map(w => w + 's');
console.log(newArray); // console: ['pens', 'books', 'codes']
Array.prototype.filter
The filter()
function is what you'd use if you're creating a search bar from a given list of items, for example. The filter()
method also creates a new array by executing the passed callback on every element of the array, and keeps it in the resulting array IF and ONLY IF the element passes the Boolean test returned by the callback.
The callback passed into the filter()
method accepts any of the three arguments: item
, index
and array
; same as the map()
method.
Example 1
Given an array of costs of different products, create a new array with the costs from the input array if the cost is <= $350, and print it to the console.
Solution:
const givenArray = [390, 190, 311.85, 67, 19048, 5000, 670];
const newArray = givenArray.filter(c => c <= 350);
console.log(newArray) // console: [190, 311.85, 67];
Example 2
Given an array of objects with the city name and population, create an array with objects from the first array if the population of that particular city is >= 5 million.
Solution:
const givenArray = [
{ "name": "Shanghai", "population": 24300000 },
{ "name": "Los Angeles", "population": 3792621 },
{ "name": "New Delhi", "population": 21800000 },
{ "name": "Mumbai", "population": 18400000 },
{ "name": "Chicago", "population": 2695598 },
{ "name": "Houston", "population": 2100263 },
];
const newArray = givenArray.filter( ({ population }) => population >= 5000000);
console.log(newArray); // console: [{name: "Shanghai", population: 24300000}, {name: "New Delhi", population: 21800000}, {name: "Mumbai", population: 18400000}]
Array.prototype.reduce
The reduce()
method creates a new array, executing the callback passed into it on every element, and outputs a single value. It does something on every element and keeps a record of the calculations in an accumulator variable and when no more elements are left, it returns the accumulator.
The reduce()
function itself takes two inputs: (a) the reducer function or callback; (b) an optional starting point or initialValue
.
The reducer function or the callback accepts 4 arguments: accumulator
, currentItem
, index
, array
.
If the optional initialValue
is given, the accumulator
at the first iteration will be equal to the initialValue
and the currentItem
will be equal to the first element in the array. Otherwise, the accumulator
would be equal to the first item in the input array, and the currentItem
will be equal to the second item in the array.
Sounds confusing? Let's have a look at two examples:
Example 1
(i) Given an array of numbers, find the sum of every element in the array, and log it to the console.
Solution:
const givenArray = [1, 2, 3, 4, 5];
const sum = givenArray.reduce((acc, curr) => acc + curr);
console.log(sum); // console: 15
Let's have a look at the accumulator and the current value...:
- at the first iteration:
acc=1
(givenArray[0])
,curr=2
(givenArray[1])
- at the second iteration:
acc=3
(givenArray[0] + givenArray[1])
,curr=3
(givenArray[2])
- at the third iteration:
acc=6
(givenArray[0] + givenArray[1] + givenArray[2])
,curr=4
(givenArray[3])
- at the fourth iteration:
acc=10
(givenArray[0] + givenArray[1] + givenArray[2] + givenArray[3])
,curr=5
(givenArray=[4])
- finally:
acc=15
(sum of all elements) (array iteration ended)
You can view this yourself by running a console.log
inside the function like so: console.log("iteration: acc="+acc+" curr="+curr);
(ii) Given an array of numbers, find the sum of every element in the array, starting with 8, and log the result to the console
Solution:
const givenArray = [1, 2, 3, 4, 5];
const sum = givenArray.reduce((acc, curr) => acc + curr, 8);
console.log(sum); // console: 23
Note: Here, we are passing the optional initialValue
parameter to the reduce()
function, saying that we want to start with 8 and do whatever we want inside the callback.
Again, you can test the values of acc
and curr
and by adding a console.log
like above.
Example 2
Given an array of numbers, find the average of them, and log it to the console.
Solution:
const givenArray = [1, 2, 3, 456, 108115, 45909.15154, 1988.1545, 145e8];
const average = (givenArray.reduce((acc, curr) => acc + curr)) / givenArray.length;
console.log(average); // console: 1812519559.288255
If you're confused from the third line, it basically calculates the sum first, and divides the return value by the length of givenArray
. You could also use:
const givenArray = [1, 2, 3, 456, 108115, 45909.15154, 1988.1545, 145e8];
const average = givenArray.reduce((acc, curr, index) => ( index == (givenArray.length -1) ) ? (acc + curr) / givenArray.length : acc + curr);
console.log(average); // console: 1812519559.288255
This is a complete no-no for readability and for the KISS principle, but I ain't a cop, use whichever method you like 😉
Array.prototype.forEach
The forEach
method is similar to the for(let i = 0; i < array.length, i++){}
syntax. It loops through the array and runs the given callback for each of the elements of the array.
The callback function passed to the forEach
function can accept the currentItem
, index
, array
.
Example
Given an array of numbers, log every number to the console(wat?!).
Solution:
const arr = [1, 2, 3, 4, 5, 6, 7, 8];
arr.forEach(val => console.log(val));
/* console:
1
2
3
4
5
6
7
8
*/
The big difference between the map
and forEach
method is that the map
method creates a new array, "mapping" the return value of the callback and create a new array, while the forEach
method just iterates over the array.
Array.prototype.some
and Array.prototype.every
The some
method tests whether at least one element of the array complies with the given test in the callback, and returns true
or false
.
The callback function passed to the some
function can accept the currentItem
, index
, array
.
Example 1
Given two arrays of numbers, test whether each of the arrays have at least one number that is > 5, and log the result to the console.
Solution:
const givenArray1 = [1, 2, 3, 5, 8];
const givenArray2 = [1, 2, 3, 4, 5];
const testArray1 = givenArray1.some(n => n > 5);
const testArray2 = givenArray2.some(n => n > 5);
console.log(`givenArray1: ${testArray1}; givenArray2: ${testArray2}`); // console: givenArray1: true; givenArray2: false
The every
method is pretty similar to the some
method, but it tests whether all the elements of the array complies with the given test in the callback, and returns true
or false
.
The callback function passed to the every
function can accept the currentItem
, index
, array
.
Example 2
Given two arrays of numbers, test whether each of the arrays have all the numbers >= 5, and log the result to the console.
Solution:
const givenArray1 = [10, 9, 8, 7, 6];
const givenArray2 = [5, 1, 2, 785, 45];
const testArray1 = givenArray1.every(n => n > 5);
const testArray2 = givenArray2.every(n => n > 5);
console.log(`givenArray1: ${testArray1}; givenArray2: ${testArray2}`); // console: givenArray1: true; givenArray2: false
Array.prototype.flat
and Array.prototype.flatMap
The flat
method creates a new Array with all the elements, and if the element is an array, then it "flattens" the element and adds all the sub-array elements to the returned array. By default, it will only flatten the array upto 1 level.
The flat
method can accept only one optional argument, the level or depth
up till which the array will be "flattened".
Example 1
Given an array of arrays of numbers, find the sum of every number inside the array as well as the sub-arrays, and log the sum to the console.
Solution:
const givenArray = [
[1, 2, 3, 4, 5, 6],
[10, 20, 30, 40, 50, 60],
[100, 200, 300, 400, 500, 600]
];
const sum =
givenArray
.flat() // flattens the array
.reduce((acc, curr) => acc + curr); // finds the sum
console.log(sum); // console: 2331
The flatMap
method is the combination of the flat
method and the map
method. It first "flattens" the array, runs the callback for each element and "maps" the return value to the corresponding element, and finally returns the "flattened and mapped" array. If you use something like: arr.flatMap(...)
, its equivalent to arr.flat().map(...)
. But, there's one catch: you cannot flatten the array by levels more than one, for that you need to use the .flat(...).map(...)
syntax.
The flatMap
method accepts the same arguments as the map
method, and so does the callback.
Example 2
Given an array of arrays of users, create a new array with a single list of all the first names of the users, and log it to the console.
Solution:
const users = [
[
{ "firstName": "Lorem", "lastName": "Ipsum" },
{ "firstName": "Dolor", "lastName": "Sit" },
{ "firstName": "Amet", "lastName": "Consectetur" }
],
[
{ "firstName": "Adipiscing", "lastName": "Elit" },
{ "firstName": "Etiam", "lastName": "Lobortis" },
{ "firstName": "Lorem", "lastName": "Elit" }
],
[
{ "firstName": "Lorem", "lastName": "Ipsum" },
{ "firstName": "Dolor", "lastName": "Sit" },
{ "firstName": "Amet", "lastName": "Consectetur" }
],
[
{ "firstName": "Adipiscing", "lastName": "Elit" },
{ "firstName": "Etiam", "lastName": "Lobortis" },
{ "firstName": "Lorem", "lastName": "Elit" }
]
];
const usersFirstNames = users.flatMap(usersGroup => usersGroup.map(u => u.firstName));
console.log(usersFirstNames); // console: ["Lorem", "Dolor", "Amet", "Adipiscing", "Etiam", "Lorem", "Lorem", "Dolor", "Amet", "Adipiscing", "Etiam", "Lorem"]
Array.prototype.find
The find
method returns the first element of the array that satisfies the Boolean test in the callback. If no element passes the Boolean test, undefined
is returned.
The callback passed to the find
function can accept any of the three arguments: item
, index
, array
.
Example
Given an array of objects with fruits, find the 'apples' and log the corresponding object to the console.
Solution:
const fruits = [
{"name": "bananas", "quantity": 8},
{"name": "cherries", "quantity": 3},
{"name": "apples", "quantity": 80}
];
const apples = fruits.find( ({name}) => name == "apples" );
console.log(apples); // console: {"name": "apples", "quantity": 80}
Array.prototype.sort
The sort
method is self-explanatory: it "sorts" an array in place and returns a sorted the array. The default sort order is ascending.
Note the words "in place". It means that the original array is changed and the same reference to the array is returned. So, originalArray===newArray
, if nothing is sorted.
It takes a Function that specifies the criteria of sorting.
Example 1
Given an array of numbers, sort the array by ascending order and log the sorted array to the console.
Solution:
const givenArray = [4, 5, 2, 1, 3];
givenArray.sort((a, b) => a - b);
console.log(givenArray);
Example 2
Given an array of contacts, sort them in alphabetical order by name, and log the sorted array.
Solution:
const givenArray = [
{"name": "Yosha Gamuda", "phone": 1234567890},
{"name": "Portia Umeng", "phone": 4894759371},
{"name": "Yosha Gamuda", "phone": 1234567890},
{"name": "Portia Umeng", "phone": 4894759371}
];
givenArray.sort(({name1}, {name2}) => {
name1 = name1.toUpperCase();
name2 = name2.toUpperCase();
return (name1 < name2) ? -1 : (name1 > name2) ? 1 : 0);
});
console.log(givenArray);
The way sort()
works is a bit different than the others. Quoting this MDN doc:
If compareFunction is supplied, all non-undefined array elements are sorted according to the return value of the compare function (all undefined elements are sorted to the end of the array, with no call to compareFunction). If a and b are two elements being compared, then:
- If compareFunction(a, b) returns less than 0, sort a to an index lower than b (i.e. a comes first).
- If compareFunction(a, b) returns 0, leave a and b unchanged with respect to each other, but sorted with respect to all different elements. Note: the ECMAScript standard only started guaranteeing this behavior in 2019, thus, older browsers may not respect this.
- If compareFunction(a, b) returns greater than 0, sort b to an index lower than a (i.e. b comes first).
- compareFunction(a, b) must always return the same value when given a specific pair of elements a and b as its two arguments. If inconsistent results are returned, then the sort order is undefined.
To compare numbers instead of strings, the compare function can subtract b from a. The following function will sort the array in ascending order (if it doesn't contain Infinity and NaN).
Conclusion
I know this article has ABSOLUTELY LEVIATHAN AMOUNT of information. There are loads of others, but essentially, you need not know every method in Array.prototype
to work with arrays. Thank you for reading this article, I hope you enjoyed it. Any feedback will be much, much appreciated: good or bad ;)
Top comments (4)
this is really helpful thanks for writing :D
Shrihan, I found that in example two of the every method you have a typo. Your some needs to be changed to every to get the result:
const givenArray1 = [10, 9, 8, 7, 6];
const givenArray2 = [5, 1, 2, 785, 45];
const testArray1 = givenArray1.some(n => n > 5);
const testArray2 = givenArray2.some(n => n > 5);
console.log(
givenArray1: ${testArray1}; givenArray2: ${testArray2}
); // console: givenArray1: true; givenArray2: falseWow, how come I didn't notice it? Fixed it now, thanks so much!
I'm still learning and this has helped me a lot. I read the MDN docs, but your examples were easier to digest. Thank you.