Introduction
Let's discuss a few common JavaScript array methods seen regularly in React.
But first, who is this post for? If you are new to JavaScript and/or React, and maybe unsure what you need to know to get started. This post is Part II in a series called Essential JavaScript Building Blocks for React, and we will take a look at several array methods, what they do, and how we use them in React.
JavaScript has A LOT of array methods. This handy article by Mandeep Kaur briefly describes 20 different array methods.
This post, however, covers four array methods in more detail:
- .map()
- .filter()
- .find()
- .reduce()
And will reference a CodeSandbox that I created specifically for this series, with working examples of each array method.
.map()
Others may disagree, but I use the map() array method more frequently than any other. What does map() do? According to the MDN Web Docs:
The map() method creates a new array populated with the results of calling a provided function on every element in the calling array.
Ugh. If you are anything like me, "doc language" isn't the easiest to understand, especially when you're a newbie.
According to me:
map() takes an existing array, does something to each item in that array, and returns an entirely new array of the EXACT SAME LENGTH, a key attribute of map().
Let's perform a map()
on this coolArray
.
const coolArray = [1, 2, 3, 4, 5]
const newerCoolerArray = coolArray.map((number) => number * 100)
console.log(newerCoolerArray)
> [100, 200, 300, 400, 500]
console.log(coolArray)
> [1, 2, 3, 4, 5]
Notice that when you console.log(coolArray)
it still holds the original values [1, 2, 3, 4, 5]
.
It's important to reiterate that map() always returns a new array of the exact same length as the original.
coolArray.length === newerCoolerArray.length
> true
.map() in React
So how does map() pertain to React? A common pattern you will see with React is "mapping" data to various HTML elements, in order to display information to the user.
Let's check out that CodeSandbox! In App.js
I created an array called fruits
:
const fruits = [
"red apple 🍎",
"green apple 🍏",
"orange 🍊",
"strawberry 🍓",
"kiwi 🥝",
"banana 🍌",
"pineapple 🍍",
"peach 🍑",
"watermelon 🍉",
"mango 🥭",
"pear 🍐",
"grapes 🍇",
"cherries 🍒",
"lemon 🍋",
"melon 🍈",
"coconut 🥥"
];
Which I pass down to my array method components, including Map.js
.
export default function Map(props) {
return (
<>
<p className="method-header">
The FRUITS array has been mapped to paragraph tags below
</p>
<p className="method-description">
The .map method iterates over each item in the "fruits" array and applies the same function/logic to each item. Here we are creating a new paragraph with the p tag for each fruit in our array.
</p>
<div className="list-card">
{props.fruits.map((fruit) => (
<p key={fruit}>{fruit}</p>
))}
</div>
</>
);
}
The key part of this component is:
<div className="list-card">
{props.fruits.map((fruit) => (
<p key={fruit}>{fruit}</p>
))}
</div>
Let's break it down: inside a <div>
we grab the fruits
array passed down as props from App.js
, and perform our map() to iterate over each fruit
in the array, creating a new <p>
for each item. Remember that map() accepts a function that it applies to each item in the array. In this case the function is simply us returning a <p>
tag.
If you navigate to the CodeSandbox link and select the ".map()" button, you will see our <div className="list-card">
populated with a new line for each fruit! Cool, huh?
With map() you can easily render similarly grouped data to your user. If the array updates somewhere else, it will update in your component! Mapping gives you a handy way to display information without having to manually add a new HTML element for each entry.
.filter()
What if you want to display specific items in your array, and not the whole kit and caboodle?
Enter the filter() method, a very powerful JavaScript function that you will see quite a lot.
From the MDN Web Docs (yayyyy):
The filter() method creates a new array with all elements that pass the test implemented by the provided function.
And my definition:
filter() operates on an existing array, evaluates each item in the array, and whichever items meet the criteria that you define, adds those items to a new array.
Welcome back, coolArray
!
const coolArray = [1, 2, 3, 4, 5]
const filteredCoolArray = coolArray.filter((number) => number > 3)
console.log(filteredCoolArray)
> [4, 5]
console.log(coolArray)
> [1, 2, 3, 4, 5]
So what is happening here? filter() takes a function (number) => number > 3
and uses that function to check against each item (number) in the array. Our function asks is the current item in the array greater than 3?
If you were to console.log
inside the filter() you would see that each item is being evaluated to true
or false
. Any item that evaluates to true
is added to the new array.
coolArray.filter((number) => console.log(number > 3))
> false // 1 is not greater than 3
> false // 2 is not greater than 3
> false // 3 is not greater than 3
> true // 4 is greater than 4
> true // 5 is greater than 4
And it's pretty obvious here, but we still want to highlight that the main difference between map() and filter() is that almost always filter() returns a new, SHORTER array than the orginal.
coolArray.length
> 5
filteredCoolArray.length
> 2
coolArray.length === filteredCoolArray.length
> false
.filter() in React
Take a look at Filter.js. There is a lot going on here, especially if you're new to React. But let's focus on line 20:
const filteredByLength = props.fruits.filter((fruit) => fruit.length > 10);
Inside props.fruits.filter()
we pass the function (fruit) => fruit.length > 10
which asks, "Is the current fruit greater than 10 characters long?"
console.log(filteredByLength)
> ["red apple 🍎", "green apple 🍏", "strawberry 🍓", "pineapple 🍍", "watermelon 🍉", "cherries 🍒"]
filteredByLength.length
> 6 // six fruits evaluate to "true" and make it into the new array
From there we can use our favorite map() method on the filteredByLength
array to render only the 6 fruits that are longer than 10 characters:
<div className="list-card">
{filteredByLength.map((fruit) => (
<p key={fruit}>{fruit}</p>
))}
</div>
Next I demonstrate how to combine filter() and includes().
Bonus content!
Let's briefly talk about includes().
From the MDN Docs:
The includes() method determines whether an array includes a certain value among its entries, returning
true
orfalse
as appropriate.
const coolArray = [1, 2, 3, 4, 5]
console.log(coolArray.includes(3))
> true
console.log(coolArray.includes(6))
> false
If any item in a given array satisfies the condition, the return value is true
. If no item in the array satisfies the condition, the return value is false
.
When you inject this functionality inside of a filter(), each iteration of the filter() method will check against each single item.
If the return from the includes() is true
inside a filter(), that specific item is added to the new array generated by filter().
Take a look at this smaller fruits
array. The filter() method iterates through the array, checking to see if each item in the array includes() a "w"
. We can see that 2 items contain a "w"
.
fruits = ["apple", "pear", "kiwi", "watermelon"]
fruits.filter(fruit => fruit.includes("w"))
> (2) ["kiwi", "watermelon"]
Back to our regularly scheduled programming
Take a look at how I use filter() and includes() in my Codesandbox to find only fruits with the word "apple"
in them.
const appleFilter = props.fruits.filter((fruit) => fruit.includes("apple"));
Which gives us three fruits:
red apple 🍎
green apple 🍏
pineapple 🍍
And map() to <p>
tags like the filteredByLength
array:
<div className="list-card">
{appleFilter.map((fruit) => (
<p key={fruit}>{fruit}</p>
))}
</div>
Lastly, I wired up a simple form that stores a user's input in local state, query
. A function findFruit()
is called on submit:
const findFruit = (e) => {
e.preventDefault();
if (query === "") {
setFilteredFruits([]);
} else {
setFilteredFruits(props.fruits.filter((fruit) => fruit.includes(query)));
}
};
Now you can see in real time that when you select the filter() tab, there is an input at the bottom. Type in a character or two and hit submit. This is essentially how a search function works!
.find()
Sometimes when you are working with an array, you want only one matching item.
From MDN Web Docs
The find() method returns the value of the first element in the provided array that satisfies the provided testing function. If no values satisfy the testing function, undefined is returned.
And my defintion:
find() searches through an existing array, checking each item in the array against a function that you provide, and the FIRST item that returns
true
is returned. At this point find() stops checking for any more matches.
Let's view an example with coolArray
.
const coolArray = [1, 2, 3, 4, 5]
const greaterThanTwo = coolArray.find(number => number > 2)
console.log(greaterThanTwo)
> 3
3
is the first item in the array that satisfies the logic number => number > 2
.
And confirming that find() returns the first item that satisfies true
coolArray.find((number) => console.log(number > 2))
> false // 1 is not greater than 2
> false // 2 is not greater than 2
> true // 3 is greater than 2 <-- RETURNED
> true // 4 is greater than 2
> true // 5 is greater than 2
.find() in React
When working with React, you often render specific data based on specific needs/requirements. Like we saw with filter(), we rendered <p>
tags of fruits that met a certain requirement.
Similarly, you may want to show only the first matching item from an array.
In the Codesandbox, under the ".find()" tab, I copy/paste the input form and functions from Filter.js
into Find.js
and change the filter() method to find().
Now when a user types in a single character, a few, or the entire matching phrase, only one fruit will ever be returned. The first match will always be whatever comes first in the array.
const fruits = [
"red apple 🍎",
"green apple 🍏",
"orange 🍊",
"strawberry 🍓",
"kiwi 🥝",
"banana 🍌",
"pineapple 🍍",
"peach 🍑",
"watermelon 🍉",
"mango 🥭",
"pear 🍐",
"grapes 🍇",
"cherries 🍒",
"lemon 🍋",
"melon 🍈",
"coconut 🥥"
];
const findAFruit = fruits.find((fruit) => fruit === "apple")
console.log(findAFruit)
> "red apple 🍎"
Even though three of our fruits contain the characters "apple", "red apple 🍎"
is the first matching item in our fruits
array.
mental break time
Let's take a moment to enjoy this relaxing gif of a sunset over an ocean pier. We're about to take a look at our final array method, .reduce()
and it's a doozy. Take as long as you need. When you are feeling thoroughly relaxed, we'll dive in...
.reduce()
The reduce() method is incredibly powerful, but can be intimidating to beginners. I am STILL intimidated sometimes! The most important thing to remember about reduce() is it operates on every single item in an array, and returns a single value. In other words, it takes all the items in your array and REDUCES them down to one single item. reduce() gives you a lot of control on how you can do achieve the desired end result.
From the MDN Web Docs:
The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in a single output value.
Check out this example with coolArray
to reduce all the numbers to a single value:
const coolArray = [1, 2, 3, 4, 5]
const reduceCoolArray = coolArray.reduce((accumulator, currentValue) => {
return accumulator + currentValue
}, 0)
console.log(reduceCoolArray)
> 15
// Each argument's current value as it steps through the array:
// Pass 1. accumulator = 0, currentValue = 1, return value = 1.
// Pass 2. accumulator = 1, currentValue = 2, return value = 3.
// Pass 3. accumulator = 3, currentValue = 3, return value = 6.
// Pass 4. accumulator = 6, currentValue = 4, return value = 10.
// Pass 5. accumulator = 10, currentValue = 5 final return value = 15.
Whew. A lot to unpack here. According to the MDN docs:
The reducer function takes four arguments
- Accumulator
- Current Value
- Current Index
- Source Array
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.
For now, we will just focus on the Accumulator and Current Value arguments.
Let's break down the above code snippet:
The 0
at the end of the provided function is the initial value at which the accumulator begins. If we change the initialValue
to something else, the accumulator begins at that value and we will receive a different final output value.
(If there is no initialValue
, the accumulator initializes as the first item in the array).
const coolArray = [1, 2, 3, 4, 5]
const startAt100 = coolArray.reduce((accumulator, currentValue) => {
return accumulator + currentValue
}, 100)
console.log(startAt100)
> 115
// The value of each argument during the iteration process:
// Pass 1. accumulator = 100, currentValue = 1, return value = 101.
// Pass 2. accumulator = 101, currentValue = 2, return value = 103.
// Pass 3. accumulator = 103, currentValue = 3, return value = 106.
// Pass 4. accumulator = 106, currentValue = 4, return value = 110.
// Pass 5. accumulator = 110, currentValue = 5 final return value = 115.
Notice that the return value from the previous iteration/call becomes the new accumulator
value.
.reduce() in React
Okay, time to be honest with y'all. I had a difficult time thinking of a good use case for the reduce() method on our fruits
array.
Thankfully my friend Katherine Peterson gave me the idea to transform the array of fruits into a single object with the fruit name as the key
and its corresponding emoji as the value
.
Kind of like this cuteAnimals
object:
cuteAnimals = {
hedgehog: "🦔",
chipmunk: "🐿️",
hamster: "🐹",
}
Navigate to the Reduce.js file and look at lines 6-12
.
const fruitsObj = props.fruits.reduce((accumulator, currentValue) => {
const fruitName = currentValue.slice(0, -3);
const fruitEmoji = currentValue.slice(-2);
const obj = { ...accumulator };
obj[fruitName] = fruitEmoji;
return obj;
}, {});
Notice the initialValue
is set to an object, {}
. If you recall, reduce() returns a single value. While an object can contain an infinite amount of information, it is still considered a single object/value.
Let's break it down:
// remove the emoji, keeping only the fruit name
const fruitName = currentValue.slice(0, -3);
// similarly, remove the fruit name, keeping only the emoji
const fruitEmoji = currentValue.slice(-2);
// create an object that updates with each pass of the accumulator
const obj = { ...accumulator };
// set the object's key to fruitName and value to fruitEmoji
obj[fruitName] = fruitEmoji;
// finally return the obj
return obj;
Now we can console.log
our fruitsObj
object.
> {red apple: "🍎", green apple: "🍏", orange: "🍊", strawberry: "🍓", kiwi: "🥝"…}
red apple: "🍎"
green apple: "🍏"
orange: "🍊"
strawberry: "🍓"
kiwi: "🥝"
banana: "🍌"
pineapple: "🍍"
peach: "🍑"
watermelon: "🍉"
mango: "🥭"
pear: "🍐"
grapes: "🍇"
cherries: "🍒"
lemon: "🍋"
melon: "🍈"
coconut: "🥥"
Woohoo! A single object with fruit names as the properties/keys and their corresponding emojis as the value!
In React you can't just render an object, or you get:
Error
Objects are not valid as a React child
So you have to get fancy with Object.entries() and map().
{`fruitsObj = {`}
{Object.entries(fruitsObj).map(([key, val]) => (
<p key={key}>
{key}: "{val}",
</p>
))}
{`}`}
Giving us:
Cool! But, not very useful on its own.
What if we used fruitsObj
to create a "search for emoji" feature? We can search by name, and if there are any matches, we get the corresponding emoji.
I use the same form from both the filter() and find() sections to grab the user's input, query
.
I decided to show the keys and values separated by columns in a table.
Check it out:
<table className="table-card">
<tbody>
<tr>
<th>FRUIT</th>
<th>EMOJI</th>
</tr>
{query && atLeastOneTrueQuery ? (
Object.entries(fruitsObj).map(([key, val]) =>
key.includes(query) ? (
<tr key={key + val}>
<td key={key}>{key}</td>
<td key={val}>{val}</td>
</tr>
) : null
)
) : (
<tr>
<td>No</td>
<td>Matches</td>
</tr>
)}
</tbody>
</table>
(I know, I know! Nested ternaries! 😱😱😱 Not the prettiest, or easiest to read. If you have a better idea how to refactor this, let me know! But it does the job for now.)
Essentially, if the user has typed in the search bar, query
updates with the user's input. If atLeastOneTrueQuery
holds at least one matching fruit, then map() and render the fruit(s) and its emoji in the table. Otherwise, render a table section that tells the user "No Matches".
Type in "g"
and you can see four fruits contain the letter "g" and now we can grab their matching emojis!
Hopefully this contrived example shows you how useful reduce() can be. There are probably a million better use cases for it. Let me know in the comments below if you've ever worked with reduce() and if you've done anything interesting with it!
Wrapping Up
If you've made it this far, GREAT JOB. And thank you! My hope is you now better understand the array methods we covered, and how you can use them in React.
I learned so much creating these examples and writing this post. The reduce() method was the hardest for me to wrap my brain around, but I feel as though I have a much better understanding when and why to use it, and how it works.
If you enjoyed this article, please like it, save it, share it! Whatever you want to do with it!
Also, follow me on Twitter, where I talk about my development journey, share anything I am working on, highlight other developers and their projects, and sometimes tweet silly memes!
When Part III in the Essential JavaScript Building Blocks for React series is released, come back and check that out!
I welcome your feedback, insight, criticism, ideas, etc! Let me know in the comments what you think!
Thank you again and BE GOOD!
Top comments (7)
Appreciate you indexing the whole thing.
Keep up with the good work sir.
Thanks!!
Your article is great
Continue......
It was the best part of your article (And my definition:)😊😊👏
lol thanks! I always try to write as conversationally and with my own personality as possible 😂
Very good article so detailed I enjoyed it.
Thank you, Andrew! It's still a work in progress in some ways, because people are generously pointing out minor issues! But I'm so glad I wrote this behemoth, because I learned a ton too.
Great effort and emphasis on the important array manipulators out there!