Unraveling Map Filter Reduce in Functional JavaScript

Map filter reduce are considered the fundamental functions in functional programming with JavaScript. In the previous post on the JavaScript Map function, we explored the map function in detail. How map works behind the scene, implementing the map function and using map() to create utility functions to make our lives easier.

We also discussed the concept of deep vs shallow copy, the identifying reference and value types, as well as their behavior in JavaScript.

Needless to say, we covered a lot of ground in the last two posts on functional programming. If you have been following through my series on functional programming, give yourself a pat on the back. Great job!

If you don’t know what functional programming is, read an introduction to functional programming before proceeding. I also recommend reading my post on map (link attached in the first sentence of this post) and going through the exercises.

You need to understand how map() and forEach() works on an intimate level to get the most out of this tutorial.

Map Filter Reduce Overview

map filter reduce all have a couple of points in common.

  • They all iterate through a list with the help of a callback function.
  • The callback function acts as an interface to manipulate the output without affecting the original list.

Since map() was covered in great depth in a previous post, we will be mainly focusing on filter and reduce throughout this post.

Despite the relative simplicity, map reduce and filter are extremely powerful functions in their own right. When combined with user-defined logic, their power is exponential.

You might have realized the following:

If I experiment with some of the concepts in these functions, I can combine functions to do some sweet things.

That is the beauty of functional programming. If done right, you can write powerful, expressive and easy to test code. And who doesn’t love that???

But in order to get there, we need to develop a strong foundation. A house with a shabby foundation will be blown down when the storm comes.

Consider map filter reduce the foundation of your functional programming knowledge. All the JavaScript that you know up until now will soon be put to the test once we start working with function composition.

Reviewing Map

For those who already have an intimate understanding of this function, forgive me. I will be brief.

map() accepts a callback function. The value returned from that callback function is mapped to the index of the list that we are operating on. For example.

var nums = [1,2,3];

/* 
 1 is multiplied by 2 = 2
 2 is multiplied by 2 = 4
 3 is multiplied by 2 = 6
*/
var mappedNums = [1,2,3].map(val, index, originalList => val * 2); // [2,4,6]
console.log(nums); // [1,2,3]

Needless to say, map is a very useful function. The more important point however, is how we utilize the map function.

Albeit the dynamic nature of JavaScript makes functional programming fairly difficult to say the least. Thankfully, the newer additions to ECMAscript such as const and Object.freeze() have made our jobs easier.

Exploring Filter()

As the method name suggests, filter() iterates through a list of items and returns a new result set comprised of items that passed a user-defined requirement.

In functional programming, the function that is passed into the filter is called the predicate. A predicate returns a boolean value. true if the condition is met, otherwise, false.

predicate == filter criteria

Naming convention wise, I think predicate functions should generally start with an “is”. Some common examples that follow the aforementioned convention include the following methods

isArray(), isBoolean(), isOddNumber()

You get the gist right?

Let us examine a simple implementation of the filter function. Please do note that I am borrowing the forEach implementation that we built in the previous post,  which is available on my GitHub account.

/**
 * @param {Object|Array} obj can either be a JavaScript object or array. 
 * @param {Function} predicate return true or truthy value to add item to result set.
 * @return {Array} fresh result after applying callback to each item
 */
function filter(obj, predicate) {
    var result = [];
    forEach(obj, function(element, index, object) {
        if (predicate(val, key, object)) {
            result.push(val);
        }
    });
    return result;
}

Just as with the map() method, the predicate function accepts three arguments

  • element: The value of the current element we are working with.
  • index: The index/key of the current element.
  • object: The original object that was passed into the function. For the sake of minimizing side-effects, I strongly recommend developers to not use this.

Heads up and Gotchas

Here is a caveat: Users are responsible for returning a truthy or falsey value. JavaScript coerces the type of the value passed into the if statement and converts it to a boolean value. If you do not want type coercion, change the following statement.

if (predicate(val, key, object)) {
    result.push(val);
}

to the following

if (predicate(val, key, object) === true) {
    result.push(val);
}

The triple equals operation ( === ) in JavaScript is a strict equality check, meaning it disallows type coercion. Below is a demonstration.

//  0 is coerced into boolean value false, since it is a "falsey" value
console.log(0 == false); // true.

// Strict equality check prevents "0" from being converted into boolean value 
// for comparison
console.log(0 === false); // false.

Identifying truthy and falsey values in JavaScript

Below is a list of falsey values. Technically, if a value is not falsey, it has to be truthy. Therefore, fortunately for us, we only need to memorize six falsey values and we know all the truthy and falsey values that JavaScript has to offer.

  • false
  • null
  • undefined
  • 0
  • NaN
  • ''
  • ""

source: MDN

Practical Application of Filter

The list below describes some common application of filter().

If you think of any other practice use cases of filter (or filter in combination with other functions), please let me know and I will add it onto the list

  • Validation checks
  • Dividing an existing list conditionally (E.g. “create a list of items that contain even numbers only”)

Exploring Reduce()

If I were to choose a black sheep out of map filter reduce, it would have to be reduce. Since you are now familiar with filter and map, I will just list the key points of reduce

  • Returns a single value (not a collection or array)
  • Accepts two arguments (not one):
    1. Callback function: has four arguments (not 3)
      1. Accumulator: The accumulator accumulates the callback’s return values; it is the accumulated value previously returned in the last invocation of the callback, or initialValue, if supplied.
      2. currentValue: The current element that is being processed.
      3. key: The index/key of the current element being processed.
      4. originalObject: The original object that is being iterated.
    2. Initial value: Value to used as the first argument of the callback.

Source: Mozilla Developer Network – reduce

In reduce, we can see a change in the structure of the code. We now have four arguments in the callback function, and second argument in the reduce() method called the intialValue.

Accumulator and Initial Value in JavaScript Reduce

Perhaps it would be faster to demonstrate the role of the accumulator and initialValue via code.

var initialValue = 0;
var sum123456 = [1,2,3,4,5,6].reduce(function(accumulator, currentValue, key, originalList) {
    accumulator += currentValue;    // increment the accumulator
    return accumulator;
}, initialValue);  // Accumulator starts off from zero.

console.log(sum123456);    // 0 + 1 + 2 + 3 + 4 + 5 + 6 = 21
console.log(initialValue); // Remains as zero

Here, we are supplying an initial value of zero. Perhaps tracking the value of the accumulator on each iteration will help you digest what is going on behind the scenes.

Iteration Accumulator currentValue index/key List
1 0 1 0 [1, 2, 3, 4, 5, 6]
2 1 2 1 [1, 2, 3, 4, 5, 6]
3 3 3 2 [1, 2, 3, 4, 5, 6]
4 6 4 3 [1, 2, 3, 4, 5, 6]
5 10 5 4 [1, 2, 3, 4, 5, 6]
6 15 6 5 [1, 2, 3, 4, 5, 6]

After the final iteration, 6 will be added onto the accumulator value 15.  After the final incrementation operation, if you take a look at the code above, we return the accumulator. Thus the value of the final return value is overwritten with the latest value.

Thus sum123456 will be equal to 21.

Note that this can be used to not only reduce a sequence of numbers but also for concatenating characters and strings.

var initialStr = "Return of the ";
var charJoin = ['j', 'a', 'y', ' ', 'l', 'e', 'e'].reduce((accumulator, currentValue) => {
    accumulator += currentValue;    // String concatenation being performed
    return accumulator;
}, initialStr);

console.log(charJoin); // Return of the the jay lee

Implementing JavaScript Reduce

Now that we know how reduce works, it is about time we worked on implementing the reduce function.

What is the major difference between reduce and result? It is the fact that we are returning a single element instead of a collection. Hence the name reduce(). We are reducing a collection to a single item.

Before moving on, we need to answer one more question.

What is the type of the value that is being returned?

Naturally, the type of the object should be the same as the initialValue. This means we need to dynamically set the return value at run-time.

var result = arguments[1];

this is where the magic happens. Pass in a number,  result is a number. Pass in a string, the result will also be a string.

function reduce(obj, cb, initialValue) {
    // This is how we set initial value;
    var result = initialValue;  
}

That was probably the most crucial part of the algorithm. Now, the rest of it should be fairly similar to the map() and filter() implementation.

Note that because we want our implementation to work for both Objects and Arrays, we are not going to create a fresh new function that accepts an object or array as its first argument. The remaining arguments will work the same as the built in reduce method available on the Array prototype chain.

/**
 * @param {Object|Array} obj can either be an object or array. Otherwise, throw error
 * @param {Function} predicate return true or truthy value to add item to result set.
 * @param initialValue the initial value we start off with. The result's value and type will be set to initialValue.
 */
function reduce(obj, cb, initialValue) {
    // This is how we set initial value. Result is initialized to initalValue.
    var result = initialValue; 
    forEach(obj, function(val, key, originalObj) {
        result = cb(result, val, key, originalObj);
    });
    return result;
}

See, it’s really not that bad. When we look at it this way, the only major difference aside from the result not being a collection of items is that the result is added as the first argument to the callback. The other three arguments are added after the first argument).

Very simple right? But the result and application range of the reduce() function varies greatly to that of filter and map.

It is truly a testament of the power of JavaScript and the sort of amazing things we can do with just a small set of functions.

Test the Reduce Implementation

Now, all we need to do is to take our reduce implementation for a test drive.

var numbers = [1,2,3,4];

var resultOfSum = reduce(numbers, function(accumulator, currentVal) {
    accumulator += currentVal;
    return accumulator;
}, 0);

console.log(resultOfSum);

var initialStr = "Return of the ";
var myName = ['j', 'a', 'y', ' ', 'l', 'e', 'e'];
var charJoin = reduce(myName, function(accumulator, currentValue) {
    accumulator += currentValue;    // String concatenation being performed
    return accumulator;
}, initialStr);

console.log(charJoin);

Instead of the following

accumulator += currentValue

return accumulator

We can easily combine this into the following. I showed you both ways so that you can examine and scrutinize the logic from multiple angles.

return accumulator + currentValue

Map Reduce Filter Combined

Learning map reduce filter is simply just the beginning. Now that we have fundamentals down, we can start doing some really cool things with these functions. In the next post on functional programming, I plan on covering composition.

In the meanwhile, I will leave you with some exercises so that you can have practice working with map reduce filter. It will help bend your mind to think more functionally.

I will mention it again because this is that important: map filter reduce are the fundamental building blocks of functional programming in JavaScript. Not learning these functions and proceeding with functional programming in JS is like trying to run without learning how to walk first.

I recommend that you take your time by practicing before moving onto some of the more advanced concepts. I will update this section periodically with more exercises so that you can get more practice.

For practicing map(), I have some exercises in my previous post (link is attached near the end of this post).

Filter Exercises

  1. Write out a polyfill for the filter method built into Array. Name the method myFilter(). E.g. Array.prototype.myFilter = function(fn) { /* code */ };
  2. Build your own implementation of filter that iterates through both JavaScript Objects (E.g. var obj = {}) and Arrays (without looking at the code snippet in previous sections).
  3. In a string, only leave the uppercase characters and remove all lower case characters. Regular non alphanumeric characters need to be filtered as well. E.g. “I am going to school. DON’T WAIT FOR mE Okay!?!” => “IDONTWAITFOREO”
  4. In the following data set
    [
    	{
    		"id": "0001",
    		"type": "donut",
    		"name": "Cake",
    		"ppu": 0.55,
    		"batters":
    			{
    				"batter":
    					[
    						{ "id": "1001", "type": "Regular" },
    						{ "id": "1002", "type": "Chocolate" },
    						{ "id": "1003", "type": "Blueberry" },
    						{ "id": "1004", "type": "Devil's Food" }
    					]
    			},
    		"topping":
    			[
    				{ "id": "5001", "type": "None" },
    				{ "id": "5002", "type": "Glazed" },
    				{ "id": "5005", "type": "Sugar" },
    				{ "id": "5007", "type": "Powdered Sugar" },
    				{ "id": "5006", "type": "Chocolate with Sprinkles" },
    				{ "id": "5003", "type": "Chocolate" },
    				{ "id": "5004", "type": "Maple" }
    			]
    	},
    	{
    		"id": "0002",
    		"type": "donut",
    		"name": "Raised",
    		"ppu": 0.55,
    		"batters":
    			{
    				"batter":
    					[
    						{ "id": "1001", "type": "Regular" }
    					]
    			},
    		"topping":
    			[
    				{ "id": "5001", "type": "None" },
    				{ "id": "5002", "type": "Glazed" },
    				{ "id": "5005", "type": "Sugar" },
    				{ "id": "5003", "type": "Chocolate" },
    				{ "id": "5004", "type": "Maple" }
    			]
    	},
    	{
    		"id": "0003",
    		"type": "donut",
    		"name": "Old Fashioned",
    		"ppu": 0.55,
    		"batters":
    			{
    				"batter":
    					[
    						{ "id": "1001", "type": "Regular" },
    						{ "id": "1002", "type": "Chocolate" }
    					]
    			},
    		"topping":
    			[
    				{ "id": "5001", "type": "None" },
    				{ "id": "5002", "type": "Glazed" },
    				{ "id": "5003", "type": "Chocolate" },
    				{ "id": "5004", "type": "Maple" }
    			]
    	}
    ]

    Filter our any batter or topping that contains ANY kind of chocolate in it. E.g. “Chocolate with sprinkles” is still considered chocolate. CREDIT: JSON data from here.

Reduce Exercises

  1. Write out a polyfill for the reduce() method built into Array. Name the method myReduce(). E.g. Array.prototype.myReduce = function(fn, initialValue) { /* code */ };
  2. Build your own implementation of reduce that iterates through both JavaScript Objects (E.g. var obj = {}) and Arrays (without looking at the code snippet in previous sections).

Source Code

All the source code for map filter reduce can be found on GitHub. I still have to code and work on the solutions. Once I finished writing out the solutions, I will upload them onto GitHub.

JavaScript Functional Programming Related Posts

As you may be well aware, this post is one post in a series of posts on functional programming. Below are other articles in the series for your reference.

Hope that you got a lot out of reading. In order to solidify what you learned, I recommend typing out the reduce function and really thinking about how this function behaves and operates. Do the same also for filter as well.

Seeing as you already know map(), this shouldn’t take long. But at the same time, hopefully, that doesn’t make you too complacent, because you actually need to use these functions in order to fully understand how to use them effectively when writing code.

Please feel free to leave a comment if you have any feedback, suggestions or advice.

Thank you. See you next time and peace!

About the Author Jay

I am a programmer currently living in Seoul, South Korea. I created this blog as an outlet to express what I know / have been learning in text form for retaining knowledge and also to hopefully help the wider community. I am passionate about data structures and algorithms. The back-end and databases is where my heart is at.

follow me on: