I have been using map, reduce, filter in my code base for a while but reading "Functional Programming in Javascript" book by Luis Atencio curated my interest in learning it's implementation. Let us understand these "immutable" (in quotes) gems better, and particularly answer why
and how
questions ?
For better understanding, let us create a class with some properties which we can refer in rest of the blog.
Step 1: Building our example
Assume new batch of software engineers are hired by XYZ company based on certain job requirements. So Developer
class is defined to capture those basic properties,
Map π―
Company decide to add bonus amount to all newly hired developers. If we want to code this then we perhaps loop through all of these objects and add the additional amount.
In our first two steps we can focus on answering why map ? and later we can look into how map works?
Step 1: Imperative Approach
though we didn't mutate the original data i.e developers, we had to calculate the length of an array and then loop through each element before adding the bonus amount. But if we decide to use map then these things will be abstracted away from us. i.e calculating the length, looping through an array, pushing it to new array. So we can focus on writing required logic. i.e adding bonus amount. Most importantly it returns a new array.
Step 2: Declarative Approach
Approach two embraces functional programming which emphasis more on composition but in either cases looping, handling temp array is abstracted away from us. Also it didn't alter the original developers
array. In short, it behaved like a black box.
For example; using map we created new pizza slices π with cheese toppings from given plain slices. As a foodie i can either eat normal plain(left) or with cheese toppings (right) slices at any given time.
I hope this helped reasoning why part of the puzzle. Next we will take a step further into knowing map's inner working.
Step 3: Map Dissection
let us implement our own map
to understand better, but first there are few things we need to consider while implementing. (a) map works on an array, (b) it doesn't mutate the original array but returns a new one, and (c) takes function as an argument to work on any given array. Based on these three pillars if we were to implement map
then it would look like below,
but we often use map
on an array, like array.map()
but in our example we have passed array as an argument. Hmmmβ¦..can we make our map behave the same as ECMAScript map ?
let us move on to next one, reduce.
Reduce β
If we have a requirement to find average salary gain per year based on experience then imperative way would be looping through each object for calculating total experience and total salary of all developers, then divide salary with total experience.
Step 1: Imperative Approach
Though we could do the calculation in one loop, we still had to worry about looping, declaring temporary variable to calculate the total.
Step 2: Declarative Approach
At first this might look little intimidating but the approach is more modular and still follows the immutable convention. ( Note: If you have better way to figure out averageSalPerYear
using reduce please let me know, happy to learn )
Step 3: Reduce Dissection
Instead of looking straight to solving our "average salary gain per year" problem, let us start with simpler example ( adding numbers ) first.
Looks good, now let us make use of "myReducer" to achieve the average salary gain from our developers array.
Great, but if we want to use myReducer
as prototype chain then the implementation would change to below ( creating new reducer as myNewReducer
to keep all implementation different from each other ),
applying both adding numbers and finding average salary gain to our latest prototype chain myNewReduce
method.
neat, now lets us move to filter.
Filter π
If we want to know all developers who has at least 3 years of experience then we can achieve that by filter, but first let us start with imperative approach.
Step 1: Imperative Approach
Looping through developers array to find years of experience and if it matches the required experience then we will add it to result array.
Step 2: Declarative Approach
quick validation to compare if the filtered data is same as imperative filter array data.
Step 3: Filter Dissection
As we did before, we will define our new filter as array prototype method so we can use like array.myNewFilter
Perhaps, if we want to handle all these scenario at once rather than operating independently. Then this opens up the conversation for method chaining
.
Chaining π
In this section we will look into "adding bonus amount to all our developers, who has at least 3 years of minimum experience and finally calculate the total salary of those developers"
Yay, we are done π
Almost, what if we want to find out junior or senior developers based on some experience criteria then we would do something like below using map, which will return new array of titles.
Nice, thats exactly what we expected. Let us check our developersCopy
array.
if you noticed the new property title
has been added to each object, but map
suppose to be immutable π€―.
Why didn't it work for us this time ? because Objects are accessed by reference. So changing the object property by reference will mutate the original array of objects ( i.e impure ).
It is possible to avoid mutation if we clone each object before applying map. For more details you can refer this medium blog or stack overflow question. So functional programming is type of implementation rather than a framework.
Lastly, these gems are very concise, and effective but this comes with a hefty price π°when used against large data set. Below is simulation of one such scenario,
Happy Coding π¦Έπ½ββοΈ
P.S : I don't understand this "Clap" thing but if you enjoyed the blog then it doesn't hurt to press it π