×
By the end of this chapter, you should be able to:
forEach
, map
, and filter
to iterate through, transform, and manipulate arraysWe've seen how to iterate through arrays using for
loops. But JavaScript also has a number of built-in methods on arrays, called iterators, which can often be used in place of for
loops. Choosing the right iterator can allow you to write code that's both shorter and easier to read. Before we get into trade offs between loops and iterators too much, though, let's take a look at the iterators available to you in JavaScript.
forEach
The first iterator we are going to look at is forEach
. As we'll see with each of these iterators, the first parameter to forEach
is a callback. This callback can have up to three parameters: the value at the current array index, the current array index, and the array itself (this will become clearer after looking at a few examples).
The most important thing to understand about forEach
is that it ALWAYS returns undefined
. With or without the return
keyword, it does not make a difference. If you need to return something from your iterator, forEach
is not the right choice for you.
Let's look at some examples:
let arr = [4,3,2,1]; arr.forEach(function(val,index,arr){ console.log(val); }); // 4 // 3 // 2 // 1 arr.forEach(function(val,index,arr){ console.log(index); }); // 0 // 1 // 2 // 3 let doubledValues = arr.forEach(function(val,index,arr){ return val*2; }); doubledValues; // undefined
It's important to note that you don't need to supply all three arguments to the callback if you don't need them all. For instance, the first example above could also be written as:
let arr = [4,3,2,1]; // the callback here just takes a single parameter, but works exactly the same as before! arr.forEach(function(val){ console.log(val); });
Now let's try to use forEach to solve a problem. Write a function called double
which accepts an array. The function should return a new array with all of the values doubled.
Here's one possible approach:
function double(arr){ let doubledArr = []; arr.forEach(function(val){ doubledArr.push(val*2); }) return doubledArr; }
Now this works totally fine, but since forEach always returns undefined
we had to go make a new array and manually push values into it. Thankfully there is a better way to do this: we can use map
!
Write a function called printFirstAndLast
which accepts an array (of objects) and console.logs a new string with the first character and the last character of each value.
printFirstAndLast(['awesome','example','of','forEach']) // ae // ee // of // fh
Write a function called addKeyAndValue
which accepts three parameters, an array (of objects), a key and a value. This function should return the array of objects after each key and value have been added to each object in the array.
addKeyAndValue([{name: 'Elie'},{name: 'Tim'},{name: 'Elie'}], "isInstructor", true) /* [ { name: 'Elie', isInstructor: true }, { name: 'Tim', isInstructor: true }, { name: 'Elie', isInstructor: true } ] */
You can read more about forEach
here.
map
Unlike forEach
, map
returns a new array of the values returned in the callback. The structure of the callback function to map
is identical to forEach
: once again, the three parameters are the value, index and array (in that order).
let arr = [1,2,3,4]; arr.map(function(val, index, array){ return val * 2; }); // [2,4,6,8] let tripledValues = arr.map(function(val,index,arr){ return val*3; }); tripledValues; // [3,6,9,12] // Here is how we can refactor our double method to use map function doubleArray(arr){ // return the result of arr.map return arr.map(function(val){ // return a new array with each value doubled return val *2; }); } doubleArray([2,4]); // [4,8]
Write a function called valTimesIndex
which accepts an array of numbers and returns a new array with each value multiplied by the index it is at in the array:
valTimesIndex([1,2,3]) // [0,2,6] valTimesIndex([5,10,15]) // [0,10,30]
Write a function called extractKey
which accepts two parameters, an array of objects, and the name of a key and returns an array with just the values for that key:
extractKey([{name: "Elie", isInstructor:true},{name: "Tim", isInstructor:true},{name: "Matt", isInstructor:true}], "name") // ["Elie", "Tim", "Matt"]
You can read more about map
here.
filter
filter
returns an array just like map
, but inside the callback you must return an expression that evaluates to true
or false
. If the expression evaluates to true
, the value will be added to the returned array.
You can think of the callback to filter
as a sort of testing function. If the element in the array passes the test, filter
keeps the element; otherwise, filter
tosses it out.
let arr = [1,2,3,4]; let valuesGreaterThanTwo = arr.filter(function(val){ return val > 2; }); valuesGreaterThanTwo // [3,4]
Write a function called filterLetters
which accepts an array of letters and returns the number of occurrences of a specific letter. This function should be case insensitive
filterLetters(["a","a","b","c","A"], "a"); // 3 filterLetters(["a","a","b","c","A"], "z"); // 0 filterLetters(["a","a","b","c","A"], "B"); // 1
Write a function called filterKey
which accepts two parameters, an array of objects, and the name of a key and returns an array with only those objects which have truthy values for that key:
filterKey([{name: "Elie", isInstructor:true, isHilarious: false},{name: "Tim", isInstructor:true, isHilarious: true},{name: "Matt", isInstructor:true}], "isHilarious") // [{name: "Tim", isInstructor:true, isHilarious:true}]
You can read more about filter
here.
When you're ready, move on to Reduce