📜 ⬆️ ⬇️

Using map and reduce in functional JavaScript

We bring to your attention translation material about using map and reduce in functional JavaScript. This article will be interesting first of all to novice developers.

Behind all these conversations about new standards, it is easy to forget that it was ECMAScript 5 that gave us a number of tools, thanks to which we can today use functional programming in JavaScript. For example, the native map () and reduce () methods based on the Array JS object. If you are still not using map() and reduce() , now is the time to start. Most modern JS platforms natively support ECMAScript 5. Using these methods will make your code much cleaner, more readable and easier to maintain. map() and reduce() will help you take the path of more elegant functional design.

Performance note


Of course, readability and maintainability of the code should not degrade performance, if the situation requires it. Modern browsers more efficiently perform more cumbersome traditional constructions, for example, cycles.

Try the following technique: first write the code based on the criteria of readability and maintainability, and then optimize its performance if there is a real need for it. Avoid premature optimization.
')
It is also worth noting that the use of methods like map() and reduce() will make it possible to derive more benefits from the improvements of the JS engine, as browsers are optimized for their use. If you do not have performance problems, it is better to write code with optimistic calculation for the future. And the techniques to improve performance, making the code less tidy, leave for later, when they need arises.

Use map


Mapping is a fundamental technique in functional programming. It is used to operate on all elements of an array in order to create another array of the same length, but with transformed content.

To make it clearer, let's consider a simple example. Suppose you have an array of words, and you need to convert it to an array containing the lengths of all the words in the source array. Yes, this is not the most relevant case, but an understanding of how this tool works will help you apply it later to improve your code.

You probably already know how to perform the described task using a for loop. For example:

 var animals = ["cat","dog","fish"]; var lengths = []; var item; var count; var loops = animals.length; for (count = 0; count < loops; count++){ item = animals[count]; lengths.push(item.length); } console.log(lengths); //[3, 3, 4] 

Several variables are defined here:


Next, we iterate every element of the array of animals : we calculate the length and put it in the lengths array.

Note: this task could be solved more concisely, without a variable element and intermediate assignment, passing the length animals[count] directly to the array lengths . The code would be a bit shorter, but less readable even in this simple example. Similarly, to slightly improve performance, one could use the known length of the animals array to initialize the lengths array as new Array(animals.length) , and then, instead of using push add elements by index. But that would also make the code a little less understandable. In general, it all depends on how you will use your code in real projects.

Technically, this is the right approach. It will work on any standard JS engine. But when you learn about map() , then the classic way will immediately seem too cumbersome.

Here's how to solve our problem using map() :

 var animals = ["cat","dog","fish"]; var lengths = animals.map(function(animal) { return animal.length; }); console.log(lengths); //[3, 3, 4] 

Here we start again with the variable for the array of animals . But besides it, we only declare lengths , and directly assign to it the result obtained by mapping an anonymous embedded function into each element of the array of animals . An anonymous function performs an operation on each animal and returns the length. Eventually, the array lengths , containing the lengths of each word, becomes the same length as the original animals .

Please note that with this approach:


Another advantage of this approach is that we can make it more flexible by dividing the named function. In this case, the code will be cleaner. Anonymous inline functions make it difficult to reuse code and may look untidy. It would be possible to define a named function getLength() and use it as follows:

 var animals = ["cat","dog","fish"]; function getLength(word) { return word.length; } console.log(animals.map(getLength)); //[3, 3, 4] 

See how cleaner the code looks. Just start applying mapping, and immediately reach a new level of functional programming.

What is a functor?


Curiously, when adding mapping to an array object, ECMAScript 5 turns the basic array type into a full functor. This makes functional programming even more accessible.

According to the classical definitions of functional programming, the functor satisfies three criteria:

  1. Contains a set of values.
  2. Implements the map function to operate on each element.
  3. The map function returns a functor of the same size.

If you want to learn more about functors, you can watch the video of Matthias Peter Johansson.

Use reduce


The reduce () method first appeared in ECMAScript 5. It is similar to map() , except that instead of creating another functor, reduce() produces a single result that can be of any type. For example, you need to get as a number the sum of the lengths of all the words in the array of animals . You will probably immediately write something like this:

 var animals = ["cat","dog","fish"]; var total = 0; var item; for (var count = 0, loops = animals.length; count < loops; count++){ item = animals[count]; total += item.length; } console.log(total); //10 

After describing the initial array, we create a variable total to calculate the amount, and assign zero to it. We also create the item variable, in which, as the for loop is executed, the result of each iteration over the array of animals stored. The count variable is used as the loop counter, and loops used to optimize iterations. We start the for loop, iterate all the words in the array of animals , assign the value of each of them to the item variable, and add the lengths of the words to the accruing total.

Again, technically everything is in order here. We processed the array, got the result. But using the reduce() method can do this much easier:

 var animals = ["cat","dog","fish"]; var total = animals.reduce(function(sum, word) { return sum + word.length; }, 0); console.log(total); 

Here, we define a new variable total and assign it the value of the result obtained after applying reduce to the array of animals using two parameters: an anonymous inline function and a cumulative total. reduce takes each element of the array, applies a function to it, and adds the result to the cumulative total, which is then passed to the next iteration. The substituted function takes two parameters: the cumulative total and the current word being processed from the array. To the length of the word function adds the current value of total .

Note that we reset the second argument to reduce() , so total is a number. The reduce() method will work without the second argument, but the result may differ from the expected. Try to determine for yourself what logic JavaScript uses when excluding total .

Perhaps the described approach looks too complicated. This is a consequence of the integrated definition of the embedded function in the reduce() method. Let's define a named function instead of an anonymous embedded function:

 var animals = ["cat","dog","fish"]; var addLength = function(sum, word) { return sum + word.length; }; var total = animals.reduce(addLength, 0); console.log(total); 

It turns out a little longer, but this is not always a disadvantage. In this case, it becomes clearer what happens with the reduce() method. It takes two parameters: the function that is applied to each element of the array, and the initial value of the accruing total. In this case, we give the name of the new function addLength initial value (zero) of the accruing total. The addLength() function also takes two parameters: a cumulative total and a string value.

Conclusion


By getting used to using map() and reduce() regularly, you can make your code cleaner, more flexible and easier to maintain. This will pave the way for you to use other functional approaches in JavaScript.

In addition to map() and reduce() , other new methods have appeared in ECMAScript 5. Probably, improving the quality of the code and the pleasure of developing, which you feel, will far outweigh the temporary degradation of performance. Use functional approaches and measure the impact on performance in real projects, instead of thinking about whether map() and reduce() are needed in your application.

Source: https://habr.com/ru/post/324342/


All Articles