Master Map & Filter, Javascript’s Most Powerful Array Functions

Learn how Array.map and Array.filter work by writing them yourself

This article is for those who have written a for loop before, but don’t quite understand how Array.map or Array.filter work. You should also be able to write a basic function. By the end of this, you’ll have a complete understanding of both functions, because you’ll have seen how they’re written.

This has been taken from Step Up Your JS: A Comprehensive Guide to Intermediate JavaScript, my online course. Feel free to view it there for interactive versions of the code samples.

Array.map

Array.map is meant to transform one array into another by performing some operation on each of its values. The original array is left untouched and the function returns a new, transformed array. For example, say we have an array of numbers and we want to multiply each number by three. We also don’t want to change the original array. To do this without Array.map , we can use a standard for-loop.

for-loop

var originalArr = [1, 2, 3, 4, 5];

var newArr = []; for(var i = 0; i < originalArr.length; i++) {

newArr[i] = originalArr[i] * 3;

} console.log(newArr); // -> [3, 6, 9, 12, 15]

Simple enough. Let’s abstract this loop into its own function so that we can turn any array we like into a new array with each element multiplied by 3. In other words, we’re trying to write a function that will take in an any array ( [1, 2, 3] ) and spit out a brand new array with its numbers multiplied by three ( [3, 6, 9] ). All we have to do is take the code we wrote above and turn it into a function so that we can reuse that loop over and over. This might seem difficult, but try to get through it.

Multiply by three

var originalArr = [1, 2, 3, 4, 5]; function multiplyByThree(arr) {

var newArr = [];



for(var i = 0; i < arr.length; i++) {

newArr[i] = arr[i] * 3;

} return newArr;

} var arrTransformed = multiplyByThree(originalArr);

console.log(arrTransformed); // -> [3, 6, 9, 12, 15]

Beautiful. Now we can pass any array into multiplyByThree and get a new array out with its values multiplied. Now, we’re going to add some code that might seem useless, but bear with me here. Let’s take a single line in that function — newArr[i] = thisItem * 3 — and turn it into its own function as well. The result will be code that is equivalent to that in the block above, but we’ll need it this way right after.

var originalArr = [1, 2, 3, 4, 5]; function timesThree(item) {

return item * 3;

} function multiplyByThree(arr) {

var newArr = [];



for(var i = 0; i < arr.length; i++) {

newArr[i] = timesThree(arr[i]);

} return newArr;

} var arrTransformed = multiplyByThree(originalArr);

console.log(arrTransformed); // -> [3, 6, 9, 12, 15]

This block does the same exact thing as the one before it. It just takes one piece out and turns it into its own function.

What if we wanted to multiply all items in an array by 5? or 10? Would we want to make a new looping function for each of those? No, not at all. That would be tedious and repetitive.

Multiply by anything

Let’s change the multiplyByThree code to be able to multiply by anything. Let’s rename it to just multiply . This is the hardest part and might take some time to wrap your head around, but try to get through it. Afterwards, it’s easy.

We’re turning this:

function multiplyByThree(arr) {

var newArr = [];



for(var i = 0; i < arr.length; i++) {

newArr[i] = timesThree(arr[i]);

} return newArr;

}

Into this.

function multiply(arr, multiplyFunction) {

var newArr = [];



for(var i = 0; i < arr.length; i++) {

newArr[i] = multiplyFunction(arr[i]);

} return newArr;

}

We’ve renamed the function and given it an extra argument to take in. That argument itself will be a function (commonly referred to as a callback). Now, we’re passing a function in to multiply ourselves, telling multiply how we want each item transformed. Using our brand new function, let’s multiply by 3 again.

var originalArr = [1, 2, 3, 4, 5]; function timesThree(item) {

return item * 3;

} var arrTimesThree = multiply(originalArr, timesThree);

console.log(arrTimesThree); // -> [3, 6, 9, 12, 15]

We’re giving our multiply function the instructions it needs to transform each value in the array by passing in the timesThree function. What if we want to multiply by 5 instead? We just give it different instructions, or a different function.

var originalArr = [1, 2, 3, 4, 5]; function timesFive(item) {

return item * 5;

} var arrTimesFive = multiply(originalArr, timesFive);

console.log(arrTimesFive); // -> [5, 10, 15, 20, 25]

It’s as simple as swapping out the timesThree function for a timesFive function. Repeating this technique, we can multiply by any number we want — we just write a new, very simple function. We’ve written one single for-loop and can multiply an array by whatever we want.

Map

Let’s make multiply even more powerful. Instead of multiplying by something, let’s allow the function to transform our array any way we want. Let’s rename multiply to, oh, I don’t know… how about map ? So, we’re turning this:

function multiply(arr, multiplyFunction) {

var newArr = [];



for(var i = 0; i < arr.length; i++) {

newArr[i] = multiplyFunction(arr[i]);

} return newArr;

}

Into this.

function map(arr, transform) {

var newArr = [];



for(var i = 0; i < arr.length; i++) {

newArr[i] = transform(arr[i]);

} return newArr;

}

Look closely at the bold and see what we changed between the multiply and map functions directly above. The only things we changed were the name of the function and the name of the second parameter that it takes in. That’s it. Turns out, multiply was already what we wanted, named differently.

We can pass in any function we want to map . We can do transform an array any way we want. Say we have an array of strings, and we want to turn them all uppercase:

function makeUpperCase(str) {

return str.toUpperCase();

} var arr = ['abc', 'def', 'ghi'];

var ARR = map(arr, makeUpperCase); console.log(ARR); // -> ['ABC', 'DEF, 'GHI']

We’ve effectively just written Array.map . Pretty neat, huh?

Using Array.map

How does the map function compare to the actual native Array.map ? The usage is slightly different. Firstly, we don’t need to pass in an array as the first argument. Instead, the array used is the one to the left of the dot. As an example, the following two are equivalent. Using our function:

function func(item) {

return item * 3;

} var arr = [1, 2, 3];

var newArr = map(arr, func); console.log(newArr); // -> [3, 6, 9]

Using the native Array.map , we don’t pass in the array. We call the Array.map method on our array and only pass in the function:

function func(item) {

return item * 3;

} var arr = [1, 2, 3];

var newArr = arr.map(func); console.log(newArr); // -> [3, 6, 9]

That’s pretty much it. You’ve written Array.map by yourself and now know how it works.

More Array.map arguments

There’s a key difference we’ve skipped over. Array.map will provide your given function with an additional two arguments: the index, and the original array itself.

function logItem(item) {

console.log(item);

} function logAll(item, index, arr) {

console.log(item, index, arr);

} var arr = ['abc', 'def', 'ghi']; arr.map(logItem); // -> 'abc', 'def', 'ghi' arr.map(logAll); // -> 'abc', 0, ['abc', 'def', 'ghi']

// -> 'def', 1, ['abc', 'def', 'ghi']

// -> 'ghi', 2, ['abc', 'def', 'ghi']

This allows you to use the index and the original array inside your transformation function if you choose. For example, say we want to turn an array of items into a numbered shopping list. We’d want to use the index:

function multiplyByIndex(item, index) {

return (index + 1) + '. ' + item;

} var arr = ['bananas', 'tomatoes', 'pasta', 'protein shakes'];

var mappedArr = arr.map(multiplyByIndex); console.log(mappedArr); // ->

// ["1. bananas", "2. tomatoes", "3. pasta", "4. protein shakes"]

Our complete map function should incorporate this functionality.

function map(arr, transform) {

var newArr = [];



for(var i = 0; i < arr.length; i++) {

newArr[i] = transform(arr[i], i, arr);

} return newArr;

}

This short function is the essence of Array.map . The actual function will have some error-checking and optimizations, but this is its core functionality.

Lastly, you can also write a function directly in the map call. Reworking our multiplyByThree example:

var arr = [1, 2, 3, 4, 5]; var arrTimesThree = arr.map(function(item) {

return item * 3;

}); console.log(arrTimesThree); // -> [3, 6, 9, 12, 15]

Done. Phew.

Array.filter

The idea here is similar to Array.map, except instead of transforming individual values, we want to filter existing values. Without any functions (besides Array.push ), say we want to filter out values in an array that are less than 5:

for-loop

var arr = [2, 4, 6, 8, 10];

var filteredArr = []; for(var i = 0; i < arr.length; i++) {

if(arr[i] >= 5) {

filteredArr.push(arr[i]);

}

} console.log(filteredArr); // -> [6, 8, 10]

Let’s abstract this to a function so we can remove values below 5 in any array.

function filterLessThanFive(arr) {

var filteredArr = []; for(var i = 0; i < arr.length; i++) {

if(arr[i] >= 5){

filteredArr.push(arr[i]);

}

} return filteredArr;

} var arr1 = [2, 4, 6, 8, 10];

var arr1Filtered = filterLessThanFive(arr1); console.log(arr1Filtered); // -> [6, 8, 10]

Let’s make it so we can filter out all values below any arbitrary value.

function isGreaterThan5(item) {

return item > 5;

} function filterLessThanFive(arr) {

var filteredArr = []; for(var i = 0; i < arr.length; i++) {

if(isGreaterThan5(arr[i])) {

filteredArr.push(arr[i]);

}

} return filteredArr;

} var originalArr = [2, 4, 6, 8, 10];

var newArr = filterLessThanFive(originalArr); console.log(newArr); // -> [6, 8, 10]

→ Abstracting out the filtering functionality

function filterBelow(arr, greaterThan) {

var filteredArr = []; for(var i = 0; i < arr.length; i++) {

if(greaterThan(arr[i])) {

filteredArr.push(arr[i]);

}

} return filteredArr;

} var originalArr = [2, 4, 6, 8, 10];

→ Filtering out anything below 5, using filterBelow

function isGreaterThan5(item) {

return item > 5;

} var newArr = filterBelow(originalArr, isGreaterThan5); console.log(newArr); // -> [6, 8, 10];

→ Filtering out anything below 7, using filterBelow

function isGreaterThan7(item) {

return item > 7;

} var newArr2 = filterBelow(originalArr, isGreaterThan7); console.log(newArr2); // -> [8, 10];

filter

So we have a function filterBelow that will filter out anything below a certain value, based on the greaterThan function we give it (this is identical to the filterBelow function above):

function filterBelow(arr, greaterThan) {

var filteredArr = []; for(var i = 0; i < arr.length; i++) {

if(greaterThan(arr[i])) {

filteredArr.push(arr[i]);

}

} return filteredArr;

}

Let’s rename it.

function filter(arr, testFunction) {

var filteredArr = []; for(var i = 0; i < arr.length; i++) {

if(testFunction(arr[i])) {

filteredArr.push(arr[i]);

}

} return filteredArr;

}

And we’ve written filter . It’s basically the same as Array.filter , again except for usage:

var arr = ['abc', 'def', 'ghijkl', 'mnopuv']; function longerThanThree(str) {

return str.length > 3;

} var newArr1 = filter(arr, longerThanThree);

var newArr2 = arr.filter(longerThanThree); console.log(newArr1); // -> ['ghijkl', 'mnopuv']

console.log(newArr2); // -> ['ghijkl', 'mnopuv']

Again, Array.filter passes in the index and the original array to your function.

function func(item, index, arr) {

console.log(item, index, arr);

} var arr = ['abc', 'def', 'ghi']; arr.filter(func); // -> 'abc', 0, ['abc', 'def', 'ghi']

// -> 'def', 1, ['abc', 'def', 'ghi']

// -> 'ghi', 2, ['abc', 'def', 'ghi']

So we should make our function do the same.

function filter(arr, testFunction) {

var filteredArr = []; for(var i = 0; i < arr.length; i++) {

if(testFunction(arr[i], i, arr)) {

filteredArr.push(arr[i]);

}

} return filteredArr;

}

Wow. That’s it. You’ve learned how to use and write Array.map and Array.filter . Go write some code.

Notes

If there’s interest in covering Array.forEach or Array.reduce as well, let me know in the comments. forEach is pretty similar to map and filter, but reduce is a bit more tricky.

If you liked this, please press the heart and feel free to check out my other publications.