jamesncox

James Cox

Posted on April 28, 2021

Array Methods

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.

Alt Text

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]


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

.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 πŸ₯₯"
];


Enter fullscreen mode Exit fullscreen mode

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>
    </>
  );
}


Enter fullscreen mode Exit fullscreen mode

The key part of this component is:



<div className="list-card">
  {props.fruits.map((fruit) => (
    <p key={fruit}>{fruit}</p>
  ))}
</div>


Enter fullscreen mode Exit fullscreen mode

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]


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

.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);


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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>


Enter fullscreen mode Exit fullscreen mode

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 or false as appropriate.



const coolArray = [1, 2, 3, 4, 5]

console.log(coolArray.includes(3))
> true

console.log(coolArray.includes(6))
> false



Enter fullscreen mode Exit fullscreen mode

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"]



Enter fullscreen mode Exit fullscreen mode

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"));


Enter fullscreen mode Exit fullscreen mode

Which gives us three fruits:



red apple 🍎
green apple 🍏
pineapple 🍍


Enter fullscreen mode Exit fullscreen mode

And map() to <p> tags like the filteredByLength array:



<div className="list-card">
  {appleFilter.map((fruit) => (
    <p key={fruit}>{fruit}</p>
  ))}
</div>


Enter fullscreen mode Exit fullscreen mode

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)));
    }
  };


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

.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 🍎"


Enter fullscreen mode Exit fullscreen mode

Even though three of our fruits contain the characters "apple", "red apple 🍎" is the first matching item in our fruits array.

mental break time

Relaxing gif of ocean waves under a pier at sunset

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.


Enter fullscreen mode Exit fullscreen mode

Whew. A lot to unpack here. According to the MDN docs:

The reducer function takes four arguments

  1. Accumulator
  2. Current Value
  3. Current Index
  4. 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.


Enter fullscreen mode Exit fullscreen mode

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: "🐹",
}


Enter fullscreen mode Exit fullscreen mode

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;
}, {});


Enter fullscreen mode Exit fullscreen mode

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);


Enter fullscreen mode Exit fullscreen mode


// similarly, remove the fruit name, keeping only the emoji
const fruitEmoji = currentValue.slice(-2);


Enter fullscreen mode Exit fullscreen mode


// create an object that updates with each pass of the accumulator
const obj = { ...accumulator };


Enter fullscreen mode Exit fullscreen mode


// set the object's key to fruitName and value to fruitEmoji
obj[fruitName] = fruitEmoji;


Enter fullscreen mode Exit fullscreen mode


// finally return the obj
return obj;


Enter fullscreen mode Exit fullscreen mode

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: "πŸ₯₯"


Enter fullscreen mode Exit fullscreen mode

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 


Enter fullscreen mode Exit fullscreen mode

So you have to get fancy with Object.entries() and map().



{`fruitsObj = {`}
{Object.entries(fruitsObj).map(([key, val]) => (
  <p key={key}>
    {key}: "{val}",
  </p>
))}
{`}`}


Enter fullscreen mode Exit fullscreen mode

Giving us:

Screen shot of fruitsObj object

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>


Enter fullscreen mode Exit fullscreen mode

(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!

Screenshot of search function on fruits array reduced to single object

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!

πŸ’– πŸ’ͺ πŸ™… 🚩
jamesncox
James Cox

Posted on April 28, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related