{ forEach, Map, and Filter. }

Objectives:

By the end of this chapter, you should be able to:

  • Use forEach, map, and filter to iterate through, transform, and manipulate arrays

We've seen how to iterate through arrays using for loops. We also saw that jQuery comes with a few functions that iterate through jQuery objects, like each, map, and filter. 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 (somewhat analogous to jQuery's each method). 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).

Note that the order of arguments in the callback is slightly different than what we saw in jQuery. With jQuery's each method, for example, the callback took the index as the first argument and the element as the second. With the built-in forEach method, this order is reversed: the array element is the first argument, followed by the index. (This will be true for JavaScrip's built-in map and filter methods as well.)

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:

var 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

var 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:

var 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){
    var 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!

Exercises

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

var arr = [1,2,3,4];
arr.map(function(val, index, array){
    return val * 2;
}); // [2,4,6,8]

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

Exercises

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.

var arr = [1,2,3,4];
var valuesGreaterThanTwo = arr.filter(function(val){
    return val > 2; 
});

valuesGreaterThanTwo // [3,4]

Exercises

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.

For solutions to the exercises in this chapter, click here.

When you're ready, move on to Reduce

Continue