Using .map(), .filter(), and .reduce() in JavaScript, with practical examples in React

Using .map(), .filter(), and .reduce() in JavaScript, with practical examples in React

In this article, I will be going over the basics of three very common functions I use in my React projects: .map(), .filter(), and .reduce(). These three functions are very useful, and you will find yourself using them quite frequently in your React projects (or any other framework).

This tutorial is kinda centered around React, but I go over the actual JavaScript functions here, so you can use them in any other front-end framework as well.

The .map() JavaScript function

Let's say you're building a React project, and it is a note editor. Say you've successfully retrieved the Document objects from the database, and on the /documents page, you want to list the notes that the user has created, so they can view or edit each of them. How can you achieve this?

This is where the JavaScript .map() function comes in, and it's very simple to use!

The thumbnail for this article was a bit misleading, the .map() function doesn't have anything to do with geographical locations. Think of it as a function, where it maps inputs to desired outputs.

In JavaScript, the .map() function creates a new array from calling a function for every array element. Note that it does not alter the original array, and it also doesn't execute the function for empty elements. You are pretty much mapping each element of an array to another array, presumably changing up each element in the process.

The syntax is as follows:

array.map(function(currentValue, index, arr), thisValue)

The parameters are as follows:

ParameterDescription
function()This is required. It is the function to be run for each array element
currentValueThis is required. This is the current value or element.
indexIndex is optional. This is the index of the current element. You may want to use this in the function.
arrThis is optional as well. This is the array of the current element.
thisValueThis is optional as well. The default value for it will be undefined. This is a value passed to the function to be used as its this value.

The .map function will return an array with the results.

Here is an example of the map() function in action:

const cars = [
    { id: 1, make: 'Toyota', model: 'Corolla', year: 2016, price: 10000 },
    { id: 2, make: 'Toyota', model: 'Camry', year: 2018, price: 15000 },
    { id: 3, make: 'Toyota', model: 'Corolla', year: 2016, price: 10000 },
    { id: 4, make: 'Toyota', model: 'Camry', year: 2018, price: 15000 },
]

console.log(cars.map(getDescription));

function getDescription(car) {
    return `${car.make} ${car.model} ${car.year}`;
}

If we run this code, we get the output:

[
  'Toyota Corolla 2016',
  'Toyota Camry 2018',
  'Toyota Corolla 2016',
  'Toyota Camry 2018'
]

Using the .map() function in React

This is cool and all, but how do we use this in React?

You will commonly use the .map() function in React when you need to render a list of components or elements from an array. Going back to the note example, perhaps, in your NotesList component, you want to render a NotesListItem component for every Document object in an array documents.

Let's take a look at how this could work:

const NotesList = () => {
    const documents = fetchDocuments() // pretend you retrieve the notes here

    return (
        <div>
            {documents.map((document) => (
                <NotesListItem
                    key={document._id}
                    document={document}
                />
            ))}
        </div>
    )
}

So here, we are mapping documents to a list of NotesListItem components. We can do this directly in the HTML part of the JSX file. You'll notice that the function we pass into the .map() function just returns a component, the NotesListItem component.

💡
Warning: Each child in a list should have a unique "key" prop

Maybe you've seen the above warning message before. An important thing to note when using the .map() function in React is that each child in a list should have a unique "key" prop. This is so that React can uniquely identify each child, so it knows how to update each of them.

In the example above, we gave each NotesListItem a key which was each document's ID. We know for sure that these are unique, so we're good!

Let's say you want to list things that don't really have unique attributes.

const NamesList = () => {

    const names = [
        "Jeremy",
        "John",
        "Paul",
        "George",
        "Ringo",
        "Jeremy",
        "John",
    ];

    return (
        <div>
            {names.map((name) => (
                <p key={name}>
                    {name}
                </p>
            ))}
        </div>
    );
}

In this example, the names aren't unique, so if we set the key to just the name, we would get an error.

You can get around this by passing the index into the function, which will be passed into the map function. Here's what this looks like:

return (
        <div>
            {names.map((name, index) => (
                <p key={index}>{name}</p>
            ))}
        </div>
);

So now each <p> will have a unique key (relative to its siblings).

You will probably end up using the .map() function quite frequently in React, especially if you are working with ever-changing data in a database that you must display.


The .filter() JavaScript function

Let's say for the note editing app, we want to display the list of notes, but only the ones whose isArchived value is false. We can quickly filter out the array using the .filter() function in JavaScript.

The .filter() function works similarly to the .map() function. It creates a new array filled with elements that pass a test provided by the function. The function that you pass in this time will return true or false.

Note that the .filter() function doesn't change the original array, it just creates a new one.

array.filter(function(currentValue, index, arr), thisValue)

The parameters here are pretty much the same as the .map() function. In this case, function is going to be a function that we define that will "check" each element, and indicate whether it passes onto the new array or not.

Here is a basic example:

const nums = [5, 4, 3, 2, 5, 9, 7, 9, 9, 8, 13, 6, 5, 8];

const evenNums = nums.filter(num => num % 2 === 0);

const coolNums = nums.filter(num => {
    if (num > 5 && num < 7) {
        return num;
    }
});

const evenIndexNums = nums.filter((num, index) => index % 2 === 0);

console.log("Even numbers: ", evenNums);
console.log("Cool numbers: ", coolNums);
console.log("Even index numbers: ", evenIndexNums);

Here is the output for this example:

Even numbers:  [ 4, 2, 8, 6, 8 ]
Cool numbers:  [ 6 ]
Even index numbers:  [
  5,  3, 5, 7,
  9, 13, 5
]

Now let's use the .filter() method in our note editor, filtering out the notes whose isArchived is true.

const NotesListFiltered = () => {

    const notes = [
        { id: 1, content: 'Hello!', date: '2019-05-30', isArchived: true },
        { id: 2, content: 'Goodbye!', date: '2019-05-30', isArchived: false },
        { id: 3, content: 'Blah blah', date: '2019-05-30', isArchived: false },
        { id: 4, content: 'Blah blah blah', date: '2019-05-30', isArchived: true },
        { id: 5, content: 'Heehee', date: '2019-05-30', isArchived: false },
    ];

    const filteredNotes = notes.filter(note => note.isArchived === false);

    return (
        <div>
            {filteredNotes.map((note, index) => (
                <p key={index}>{note.content}</p>
            ))}
        </div>
    );
}

Here, we are using the .filter() function to create a new array called filteredNotes, whose isArchived is false. filteredNotes will end up having 3 elements. We then display those elements using the .map() function.

The .filter() function can be useful when dealing with search queries or filters. Let's say you have buttons on the screen that will filter archived notes, you can use the .filter() method to update the notes shown on the screen. Or maybe you only want to show notes from a specific date range. The .filter() method could be your solution there as well.


The .reduce() JavaScript function

The last useful function I will be going over today is the .reduce() function. The .reduce() function executes a reducer function for each element in an array. Instead of returning a new array like .filter() and .map(), it returns a single value. Just like .filter() and .map(), it does not change the original array.

array.reduce(function(total, currentValue, currentIndex, arr), initialValue)

These parameters are similar to .filter() and .map(). But here, the total parameter is required, and it is either the initialValue, or the value previously returned from the function.

Let's look at a basic example:

const nums = [1, 2, 3, 4, 5];

const sum = nums.reduce((total, current) => total + current);

console.log(sum); // 15

The reduce() function can be very useful in calculating totals from a list of objects. Let's say you have a list of documents, each with a likes value. You can easily add those up using reduce() like this:

const NotesListLikes = () => {

    const notes = [
        { id: 1, content: 'Hello!', date: '2019-05-30', isArchived: true, likes: 0 },
        { id: 2, content: 'Goodbye!', date: '2019-05-30', isArchived: false, likes: 15 },
        { id: 3, content: 'Blah blah', date: '2019-05-30', isArchived: false, likes: 420 },
        { id: 4, content: 'Blah blah blah', date: '2019-05-30', isArchived: true, likes: 10 },
        { id: 5, content: 'Heehee', date: '2019-05-30', isArchived: false, likes: 23 },
    ];

    const filteredNotes = notes.filter(note => note.isArchived === false);
    const totalLikes = filteredNotes.reduce((total, current) => total + current.likes, 0);

    return (
        <div>
            <h1>Notes</h1>
            <p>Total likes: {totalLikes}</p>
            {notes.map((note, index) => (
                <p key={index}>{note.content}</p>
            ))}
        </div>
    );
}

Maybe you want to add these values onto another value. Say you already have a number of likes on the user's other posts, like images or something, and you want to add the number of likes from the document objects to that number. You can just change the initialValue in the reduce() function to that number.

const TestComponent = () => {

    const initalLikes = 289;

    const notes = [
        { id: 1, content: 'Hello!', date: '2019-05-30', isArchived: true, likes: 0 },
        { id: 2, content: 'Goodbye!', date: '2019-05-30', isArchived: false, likes: 15 },
        { id: 3, content: 'Blah blah', date: '2019-05-30', isArchived: false, likes: 420 },
        { id: 4, content: 'Blah blah blah', date: '2019-05-30', isArchived: true, likes: 10 },
        { id: 5, content: 'Heehee', date: '2019-05-30', isArchived: false, likes: 23 },
    ];

    const filteredNotes = notes.filter(note => note.isArchived === false);
    const totalLikes = filteredNotes.reduce((total, current) => total + current.likes, initialLikes);

    return (
        <div>
            <h1>Notes</h1>
            <p>Total likes: {totalLikes}</p>
            {notes.map((note, index) => (
                <p key={index}>{note.content}</p>
            ))}
        </div>
    );
}

Bonus: Time and Space Complexity Analysis

When using built in JavaScript functions, it can be important to know the runtime and space complexity of the functions you are using.

For all three of them, the runtime complexity is O(n) (with n being the size of the list), as it just iterates through the array once.

The space complexity for .map() and .filter() are O(n), since they create brand new arrays. The space complexity of .reduce() is just O(1).

Thanks for reading!

I hope you enjoyed reading this article, and learned the basics of .map(), .filter(), and .reduce(), as well as some practical use cases for them. These three methods are powerful tools that I use very often in my React projects.