Javascript in real world
Tags: javascript, array, arraymethods, javascripttips
Published: 2020-07-05 16:15:56

If you’re starting in JavaScript, you may be busy doing DOM manipulation and playing with other browser APIs. Although, these are also a part of javascript, when it comes to industrial use of the language for frontend frameworks like React or Angular, or with backend frameworks like express.js or sails.js yo come across tons of arrays and object manipulations, and maybe you haven’t heard of .map(), .reduce(), and .filter(). If you don’t need to be compatible with this very old browser, you have to become familiar with those methods.

## .map()

Let me explain how it works with a simple example. Say you have received an array containing multiple objects — each one representing a person. But you only need the list of names from that.

    // What you have
    var officers = [
      { id: 20, name: 'Captain America' },
      { id: 24, name: 'Spiderman' },
      { id: 56, name: 'Iron Man' },
      { id: 88, name: 'Hulk' }
    ];
    // What you need
    ['Captain America', 'Spiderman', 'Iron Man', 'Hulk']

There are multiple ways to achieve this. You might want to do it by creating an empty array, then using any of .forEach(), .for(...of), or a simple .for() to meet your goal. We will do it by using **Array.prototype.map()**

    var officersNames = officers.map(function (officer) {
      return officer.name
    });

I used the function keyword to create an anonymous function here, we can even be more concise with arrow functions (requires ES6 support, Babel or TypeScript)

    const officersName = officers.map(officer => officer.name);

So how does .map() work? Basically it takes 2 arguments, a callback and an optional context (will be considered as this in the callback) which I did not use in the previous example. The callback runs for **each value in the array** and **returns each new value** in the resulting array. There can be numerous possible applications of map in a real-world application. For example in React.js to render a list of cards with these names we can simply create an array of cards using the same map, as shown below.

    const officerCards = officers.map(officer => <Card name={officer}/>);

This array can now be placed directly to render the list of cards, with each card having the names.

Keep in mind that the resulting array will always be the same length as the original array.

## .reduce()

Just like .map(), .reduce() also runs a callback for each element of an array. What’s different here is that reduce passes the result of this callback (the accumulator) from one array element to the other.

The accumulator can be anything (integer, string, object, etc.) and must be instantiated or passed when calling the function.

Time for an example! You have an array with these students and their respective age:

    var students = [
      {
        id: 10,
        name: "John Doe",
        age: 14,
      },
      {
        id: 2,
        name: "Jane Roe",
        age: 30,
      },
      {
        id: 41,
        name: "Foo",
        age: 16,
      },
      {
        id: 99,
        name: "Bar",
        age: 22,
      }
    ];

We need to know the total age of all of them. With .reduce(), it’s pretty straightforward:

    var totalAge = students.reduce(function (accumulator, student) {
      return accumulator + student.age;
    }, 0);

Notice that I’ve set the starting value as 0. I could have also used an existing variable if necessary. After running the callback for each element of the array, reduce will return the final value of our accumulator (in our case: 82).

Now again let shorten it with the ES6 arrow function.

    const totalAge = students.reduce((acc, student) => acc + student.age, 0);

Now let’s say I want to find which student is the oldest. For that, I can use reduce as well:

    var oldestStudent = students.reduce(function (oldest, student) {
      return (oldest.age || 0) > student.age ? oldest : student;
    }, {});

I named my accumulator oldest. My callback compares the accumulator to each student. If a student has more age than oldest, then that student becomes the new oldest so that’s the one I return.

As you can see, using .reduce() is an easy way to generate a single value or object from an array.

## .filter()

What if you have an array, but only want some of the elements in it? That’s where .filter() comes in!

Here’s our data:

    var pilots = [
      {
        id: 2,
        name: "Wedge Antilles",
        faction: "Rebels",
      },
      {
        id: 8,
        name: "Ciena Ree",
        faction: "Empire",
      },
      {
        id: 40,
        name: "Iden Versio",
        faction: "Empire",
      },
      {
        id: 66,
        name: "Thane Kyrell",
        faction: "Rebels",
      }
    ];

Say we want two arrays now: one for rebel pilots, the other one for imperials. With .filter() it couldn’t be easier!

    var rebels = pilots.filter(function (pilot) {
      return pilot.faction === "Rebels";
    });
    var empire = pilots.filter(function (pilot) {
      return pilot.faction === "Empire";
    });

That’s it! And it’s even shorter with arrow functions:

    const rebels = pilots.filter(pilot => pilot.faction === "Rebels");
    const empire = pilots.filter(pilot => pilot.faction === "Empire");

Basically, if the callback function **returns true**, the current element **will be in the resulting array**. If it returns false, it won’t be.

## .find()

Where filter always returns an array, which can have zero, one or more than one element, find can be used to get a single element out of the array. Similar to filter() find takes a function as an argument, iterates over the elements of the array and return the first element for which the function gives return true

Let’s try on the same data as the previous one:

    var pilots = [
      {
        id: 2,
        name: "Wedge Antilles",
        faction: "Rebels",
      },
      {
        id: 8,
        name: "Ciena Ree",
        faction: "Empire",
      },
      {
        id: 40,
        name: "Iden Versio",
        faction: "Empire",
      },
      {
        id: 66,
        name: "Thane Kyrell",
        faction: "Rebels",
      }
    ];

Say we want to find the first imperial pilot, find() can do this with the following one-liner.

    pilots.find(pilot => pilot.faction === "Empire")

    // {
        id: 8,
        name: "Ciena Ree",
        faction: "Empire",
      }

## Combining .map(), .reduce(), and .filter()

Since all three are called on arrays and since .map() and .filter() both return arrays, we can easily chain our calls.

Let’s check out another example. Here’s our data:

    var personnel = [
      {
        id: 5,
        name: "Luke Skywalker",
        pilotingScore: 98,
        shootingScore: 56,
        isForceUser: true,
      },
      {
        id: 82,
        name: "Sabine Wren",
        pilotingScore: 73,
        shootingScore: 99,
        isForceUser: false,
      },
      {
        id: 22,
        name: "Zeb Orellios",
        pilotingScore: 20,
        shootingScore: 59,
        isForceUser: false,
      },
      {
        id: 15,
        name: "Ezra Bridger",
        pilotingScore: 43,
        shootingScore: 67,
        isForceUser: true,
      },
      {
        id: 11,
        name: "Caleb Dume",
        pilotingScore: 71,
        shootingScore: 85,
        isForceUser: true,
      },
    ];

Our objective: get the total score of force users only. Let’s do it step by step!

First, we need to filter out the person who can’t use the force:

    var jediPersonnel = personnel.filter(function (person) {
      return person.isForceUser;
    });// Result: [{...}, {...}, {...}] (Luke, Ezra and Caleb)

With that, we have 3 elements left in our resulting array. We now need to create an array containing the total score of each Jedi.

    var jediScores = jediPersonnel.map(function (jedi) {
      return jedi.pilotingScore + jedi.shootingScore;
    });// Result: [154, 110, 156]

And let’s use reduce to get the total:

    var totalJediScore = jediScores.reduce(function (acc, score) {
      return acc + score;
    }, 0);// Result: 420

And now here’s the fun part… we can chain all of this to get what we want in a single line:

    var totalJediScore = personnel
      .filter(function (person) {
        return person.isForceUser;
      })
      .map(function (jedi) {
        return jedi.pilotingScore + jedi.shootingScore;
      })
      .reduce(function (acc, score) {
        return acc + score;
      }, 0);

And look how pretty it is with arrow functions:

    const totalJediScore = personnel
      .filter(person => person.isForceUser)
      .map(jedi => jedi.pilotingScore + jedi.shootingScore)
      .reduce((acc, score) => acc + score, 0);

Boom! 💥

Keep coding!