.map()

Let's take a look at the last array method we covered .every. We saw that it went through every array element in the array we gave it and passed it to a custom function. But all the information we got was whether a certain item existed or not.

What if we wanted to do a different kind of operation instead? Remember how we had to directly find and update the array element we wanted to change manually? What if we had an array method that could do that for us?

Let's say that we got our temperature data wrong and every day last week was a record-breaking temperature, so we want to set isRecordTemp to true for every element. There's an array method called map that allows us to transform each element as we like and it's used just like .some or .every.

const temperatures = [
  { degrees: 69, isRecordTemp: false },
  { degrees: 82, isRecordTemp: true },
  { degrees: 73, isRecordTemp: false },
  { degrees: 64, isRecordTemp: false },
];

So we can start by saying temperatures.map and pass it a function, where again each element is a temperature.

temperatures.map(temperature);

And then for each temperature we want to get the isRecordTemp property and set it to true. We'll do that in the return:

temperatures.map((temperature) => {
  temperature.isRecordTemp = true;
  return temperature;
});

And just like .some() or .every(), the result of .map() is immediately returned to us. But what's different about map is that it transforms our array, so the return value will be an array. And note that this will be a brand new array. It doesn't mutate the old one. Let's store the result in a variable, newTemps:

const newTemps = temperatures.map((temperature) => {
  temperature.isRecordTemp = true;
  return temperature;
});
console.log(newTemps);

So now we see what map does. It applies a given transform that we specify in the callback function, or the function passed to the map method, to every single element of the array.

We can go even further. Besides modifying existing array properties, we can add entirely new ones. Say that we wanted to add the property that every day was a record high, and put it on a property isHigh that would hold the boolean value true. Take a minute and see if you can figure out how you might add a new property to each temperature element.

In fact, it would look very similar to our code before. We just map over every temperature and then take each temperature, add a new isHigh property on it and set it to true

const newTemps = temperatures.map((temperature) => {
  temperature.isHigh = true;
  return temperature;
});
console.log(newTemps);

But what if we set a property conditionally and only set isHigh to true for some elements. Let's say we know it was a record high if the temperature was above 70 degrees. How do we map over an array based on a condition?

Since map methods are very concise, we can just apply our knowledge of writing concise conditionals with the ternary operator.

If the temperature is above 70, we set isHigh to true. Otherwise, we just want to return the original temperature element. Remember from using some and every that there is an implicit return with the arrow function. To return the entire array from map, we need to return the result of every iteration.

So let's express this conditional with a ternary. We take the degrees property and see if it's greater than 70. If so, we add on the isHigh property. But we want to update this object properly, that is immutably. We know how to do that with spread operator. So we'll spread in the existing temperature data, and add on isHigh at the end. And otherwise, if the temperature is not high enough, we leave it as is and return it:

const newTemps = temperatures.map((temperature) =>
  temperature.degrees > 70 ? { ...temperature, isHigh: true } : temperature
);
console.log(newTemps);

So when we get our result, we see the two middle elements are higher than 70 and have the isHigh property.

So map is a very powerful array method, especially combined with conditionals such as the ternary and shortcircuiting. We're going to be using it a lot to transform our array data in a concise and immutable fashion.

.forEach()

And also, in addition to map, there is another array method you should be familiar with that allows us to perform operations to every element of an array, called the forEach method.

Understand that it works in a very similar way to .map(), but the big difference is that it doesn't create new arrays. It just allows us to iterate over the array we give it and perform some operation.

We usually use forEach when we already have our array in the format that we want, using methods such as map. And then using that array data, do something with it.

For example, now that we have our transformed array with two elements that we are a record high, maybe now we want to alert our user of that.

So let's take our newTemps array and iterate over it with forEach, so again for each temperature, we want to say that if it was a high tell our user that temperate X degrees was a record high last week.

So again we can call each array element temperature, and for forEach, since it doesn't have a return value, we can still use an arrow function, but we don't need an implicit return, so we can create a function body:

newTemps.forEach((temperature) => {});

And in it, we can set up a simple if statement that says, if isHigh was true, we can log the result for user with our text:

newTemps.forEach((temperature) => {
  if (temperature.isHigh) {
    console.log(
      `Temperature ${temperature.degrees} was a record high last week!`
    );
  }
});

And when we run this, we see our two logs for those two high temperatures.

Array method chaining

But we can make this even better. The great thing about methods that return new arrays like map is that we can change them. In other words, we don't have to create this intermediate variable, newTemps, we can remove that and right after map, just pass it immediately to forEach, as if it was the array:

temperatures
  .map((temperature) =>
    temperature.degrees > 70 ? { ...temperature, isHigh: true } : temperature
  )
  .forEach((temperature) => {
    if (temperature.isHigh) {
      console.log(
        `Temperature ${temperature.degrees} was a record high last week!`
      );
    }
  });

This way of composing array methods that return arrays is called chaining. And it's a great way of making your code more concise. Use it wisely though, because it can also make it harder to read. In some cases, you'll want to create an intermediate variable just for more clarity.

Review

So add these two new array methods to your toolset, map and forEach. map transforms our arrays and gives us a new one of the same length with that transformed data and forEach just iterates over each array element and applies a given action to each of them and does not create an array. Be aware that you can chain array methods that return news arrays like map() and create a series of powerful transformations.