The Power of Higher-Order Array Methods
Arrays are everywhere. From simple matrices to complex list of objects holding every little detail about each product on a page. We have to deal with them day to day and often when we operate on them, we write out the same lines of code instinctively without giving much attention to the possible forms they can take. The way you write them can determine how easy it is for others to read and understand your implementation.
Luckily in JavaScript, we have some nice built-in functions for dealing with arrays that can simplify your code, make it easier to read, and in the end, make it easier to debug in case something goes off the rails.
Functions associated with arrays are a core part of the functional programming paradigm, which is nothing more than
“a style of building the structure and elements of computer programs”- Wikipedia
It is often said that in functional programming, you declare what you mean. When you go with the imperative approach your code focuses more on the “How it is doing” things. Going with the declarative approach, it focuses more on the “What it is doing”. If you would like to see some code examples for demonstrating the difference between the two, keep on reading, I’ll do so when we get to look at the map
function.
Now to demonstrate each and every function we have, we are going to need some data to work with. Let’s imagine you are developing an e-commerce and you have an array of products. Each item holds an object that has various properties such as the name, price, id, and an optional flag if the item is on sale:
{
"products": [
{
"id": 1,
"name": "Donec",
"price": 73
},
{
"id": 2,
"name": "Vestibulum ante ipsum primis",
"price": 14,
"onSale": true
},
{
"id": 3,
"name": "Phasellus fermentum",
"price": 2.5
},
{
"id": 4,
"name": "Nullam enim. Sed nulla",
"price": 22.9
},
{
"id": 5,
"name": "Mauris",
"price": 64.3,
"onSale": true
}
]
}
If the item is not on sale, the whole flag is omitted.
Array.prototype.find
find
, as the name suggests is used for finding a specific element in an array. It returns one item that first passes the test in the callback function:
// This will return the following:
// {id: 1, name: "Donec", price: 73}
products.find(product => product.id === 1);
// Even though we have two products which are on sale, this will return:
// {id: 2, name: "Vestibulum ante ipsum primis", price: 14, onSale: true}
products.find(product => product.onSale);
// This will return undefined
products.find(product => product.id === 100);
For the first example, we have one object returned as each id is unique. Clear enough. But in the second one, we have two products which are on sale, but we still get back only the first one which passes the test. If there’s no match, it simply returns undefined
.
Array.prototype.filter
Now what if we want to find all products which are on sale and not just the first one? This is where filter
can help us:
// This will return both products that are on sale.
products.filter(product => product.onSale);
// Likewise, this will return every product which is not on sale.
products.filter(product => !product.onSale);
// If there's nothing to return, we get back an empty array: []
products.filter(product => product.price > 100);
filter
returns every element that passes the test, unlike find
which only returns the first one. If there’s no match, instead of undefined
we get []
.
Array.prototype.every
Unlike find
or filter
where we expect an item to be returned from the array, every
returns either a true
or false
value. If every item in the array passes the test, we get true
otherwise, we are left with false
:
// This produces false
products.every(product => product.onSale);
// This produces true
products.every(product => product.name);
It’s clear that not every product is on sale for a given e-commerce site so we get back false
for the first example. However, we can check if every product has a name. Since in this example they have, we get back a true
value.
Array.prototype.some
some
is very similar to every
in terms of the value returned: either returns a true
or false
. But instead of checking for every item to match the criteria, if at least one product matches the criteria, it will instantly return true
.
// We do have some products which are on sale so we get back true.
products.some(product => product.onSale);
This time, we get back true
as we are not checking if every item is on sale. We are only interested in whether there is at least one item on sale.
Array.prototype.map
Until now, we only looked at how we can filter for values in an array. Each and every function’s main purpose was to match for an item given a criteria. map
on the other hand, is used for transforming an array. We give it a callback function which will be called for every item. Inside the function, we usually do some kind of transformation. Say you want to round prices for every product:
// This will only return the prices: [73, 14, 2, 22, 64]
products.map(product => Math.floor(product.price));
// This will return all products with the prices rounded
products.map(product => {
product.price = Math.floor(product.price);
return product;
});
// Using an imperative approach, this could have been done with a for loop
for (let i = 0; i < products.length; i++) {
products[i].price = Math.floor(product.price);
}
In the first example, if we were to only return the prices, we only get back an array with the same length as the original, but with only the prices. To have the expected behavior we will need to return the product
itself at the very end. Note that how using map
can simplify your code — compared to a for loop — and make it more readable.
Array.prototype.reduce
We’ve left out reduce, the most intimidating. At least it was for me, more than every other. reduce
is used to produce a single value from an array. It operates on every item and it also requires an accumulator.
Say this time we want to get the sum of the prices of all products. With the help of reduce
. This can simply be done with one single line:
// This will return the sum of prices which will be 176.7
products.reduce((accumulated, product) => accumulated + product.price, 0);
reduce
expects two parameters, one for the callback function and one for the initial value. We start from 0
, this is our initial value. The callback function takes in two parameters, one for the accumulated value and one for the item itself. In each iteration, the accumulated
parameter gets the value produced from the previous iteration.
- At the beginning, its value will be
0
. - We then add the price of the first product to it, so its value will be
73
. - In the next iteration when the function is called, the value of
accumulated
will be73
and we add the next price tag to it and so on.
Summary
With every important array function in your tool belt, you’ll be able to make your code easier to reason about, cleaner, and last but not least, more easily testable.
If you are not utilizing them in your codebase right now, go ahead and give it a try, you may never want to look back. Thank you for taking the time to read this article, happy coding!
Rocket Launch Your Career
Speed up your learning progress with our mentorship program. Join as a mentee to unlock the full potential of Webtips and get a personalized learning experience by experts to master the following frontend technologies: