Functional Programming – Map, Reduce, Filter

There are three functions and one object type that you should care about more than anything else and those are map, reduce, filter, and the humble array. Today, we’re going to understand why they’re so important, starting with the array.

Arrays

The array is a simple structure; it lets you hold a list of items. These list of items can be accessed and moved around or looped through for various purposes. Now, what if I told you that the array had some serious power behind it? You could create objects, strings, sum numbers, and more with these three magical functions: map, reduce, and filter. Let’s start with map.

Map

Map is an array method that returns a new array after running a function on each element in the array. This can be beneficial to us in several ways. Here’s an example of adding a new property to a list of objects using map, and then using a for loop:

/* Map Example – Adding a property Business */
const employeeList = [
{ name: 'Tom', job: 'Programmer' },
{ name: 'Sam', job: 'Chef' },
{ name: 'Jennifer', job: 'Digital Artist' }
];
//Create Second List to maintain original contents
const employeeList2 = JSON.parse(JSON.stringify(employeeList));
const business = 'Sun Corp';
/* For Loop */
//Create new list to contain elements
const newEmployeeList = [];
for(let i = 0; i < employeeList.length; i++) {
const copy = JSON.parse(JSON.stringify(employeeList[i]));
copy.business = business;
newEmployeeList.push(copy);
}
console.log(employeeList);
console.log(newEmployeeList);
/* Map */
//List instantly created by map
const newEmployeeList2 = employeeList2.map((element) => {
const copy = JSON.parse(JSON.stringify(element));
copy.business = business;
return copy;
});
console.log(employeeList2);
console.log(newEmployeeList2);
view raw map-example.js hosted with ❤ by GitHub

As you can see the map approach can be simpler; you don’t need to declare “i” and you don’t need to implicitly create an array either. map creates a new array, making you like easier, and the original array stays intact (Not exactly the case with objects, but with numbers, this is the case). If you were to try to add a new element without creating a new array implicitly, you would be adding a new element to the original array, unlike map. This is beneficial to us because it prevents us from mutating objects and values in our source code. Map has a lot of uses and it’s definitely more powerful than you think. Now, let’s talk about reduce.

Reduce

Now reduce, let’s you reduce an array to a single value using a function. Furthermore, reduce let’s you pass a value as the starting value. Here’s an example:

/* Reduce Example
* Go through each element and return a value when combining the previous element with the curr element
*/
const shoppingListPrices = [10.21, 20.33, 40, 33.5, 5, 2, 7, 9, 9.99, 10.99];
/* ES6 */
const totalPrice = shoppingListPrices.reduce((start, curr) => start + curr, 0);
/* ES5 */
const totalPrice2 = shoppingListPrices.reduce(function (start, curr) {
return start + curr;
}, 0);
console.log("Shopping List",shoppingListPrices); //ShoppingList [10.21, 20.33, 40, 33.5, 5, 2, 7, 9, 9.99, 10.99]
console.log(totalPrice); //148.02
console.log(totalPrice2); //148.02
view raw reduce-example1.js hosted with ❤ by GitHub

Here we reduce the array of prices to a single value. This single value doesn’t mutate the original array. Easy right?

Let’s use a more complex example; reducing an array of objects into a single object:

/* Reduce Example 2 – Reducing to a single object */
const propertyList = [{ name: "Hero" }, { str: 100 }, { def: 200 }];
/* ES6 */
const heroStats = propertyList.reduce((start, curr) => Object.assign(start, curr), {});
/* ES5 */
const heroStats2 = propertyList.reduce(function (start, curr) {
return Object.assign(start, curr);
});
console.log(heroStats); //{name: 'Hero', str: 100, def: 200}
console.log(heroStats2); //{name: 'Hero', str: 100, def: 200}
//Change heroStats2 name property
heroStats2.name = "Rem";
console.log(heroStats); //{name: 'Hero', str: 100, def: 200}
console.log(heroStats2); //{name: 'Rem', str: 100, def: 200}
view raw reduce-example2.js hosted with ❤ by GitHub

Now, imagine trying to do this with a for loop. A for loop would probably require extra code, creating a new variable; And reduce is a lot more readable and can be used as the basis of many new functions in your code base. Now, the last one on the list filter.

Filter

Filter does exactly as the name describes; it applies a filter to every element in the array and then returns a new array with the filtered values. Plus, the original array is untouched. For us, this means we can do a lot with our arrays, but here’s an example of filtering number and objects by type:

/* Filter Example – Object and Numbers*/
const numberList = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
const types = [{ name: "Penguin", type: "Bird" }, { name: "Hopper", type: "Rabbit" }, { name: "Duck", type: "Bird" }];
/* Filtering Numbers */
/* ES5 */
const numbersAbove = numberList.filter(function (num) {
return num > 50;
});
/* ES6 */
const numbersAbove2 = numberList.filter((num) => num > 50);
console.log(numbersAbove); // [60, 70, 80, 90, 100]
console.log(numbersAbove2); // [60, 70, 80, 90, 100]
/* Filter Objects – Getting Bird */
const birds = types.filter(function (element) {
return element.type === 'Bird';
});
const birds2 = types.filter((element) => element.type === 'Bird');
console.log(birds); // [{name: 'Penguin', type: 'Bird'}, {name: 'Duck', type: 'Bird'}]
console.log(birds2); // [{name: 'Penguin', type: 'Bird'}, {name: 'Duck', type: 'Bird'}]
console.log(types); //Remains the same as the original
view raw filter-example.js hosted with ❤ by GitHub

In this example, we run our filter function, which tests if a value is true or false. This true or false test will determine what array elements pass through our filter. Now, you might be thinking none of this important, but let’s talk about composability.

Composability is one of the positives of using map, reduce, and filter. Each one returns a new array with the same set of methods (except reduce), thus you can chain these calls together. This is something you can’t do with for loops.

Here’s a real example of chaining map, reduce, and filter to create a list of stats we want:

/* Map, Reduce, Filter – Composing The functions
* Create Warrior classes using the stats below
* Filter Stats above 50 and reduce it to a single value
* called heroStat with the composition of all their stats
*/
const stats = [10, 20, 30, 50, 70, 80];
/* ES5 Example */
//Map stat numbers into class warrior object
const warriors = stats.map(function (element) { return Object.assign({ class: 'Warrior', stat: element }); });
//filter warriors with stats above 50
const heroStats = warriors.filter(function (element) { return element.stat > 50; })
.reduce(function (start, curr) {
//return warrior stats from the beginning of the way
return start.stat + curr.stat;
});
const hero = { class: 'Hero', stat: heroStats };
console.log('Hero Stats', hero); // Hero Stats { class: 'Hero', stat: 150 }
/* ES6 Example */
const warriors2 = stats.map((element) => { return Object.assign({ class: 'Warrior', stat: element }); });
console.log(warriors); // [{class: 'Warrior', stat: 10}, {class: 'Warrior', stat: 20}…]
const heroStats2 = warriors2.filter((element) => element.stat > 50)
.reduce((start, curr) => {
return start.stat + curr.stat;
});
const hero2 = { class: 'Hero', stat: heroStats2 };
console.log(hero2); // { class: 'Hero', stat: 150 }

As you can see in both examples, we use each function one after another chained with “.” notation. Because each method returns a new array (except reduce), we can chain our methods together then reduce them to a single value.

Once again, I hope you learned something and consider using these functions in the future. Stay tuned for the next tutorial on immutability and common traps. Have a wonderful day.~

%d bloggers like this: