📜 ⬆️ ⬇️

Filtering and chaining in functional JavaScript



We offer a translation of the article , which will allow you to refresh your knowledge on the topic, as well as will be useful for beginners in JavaScript, who are still mastering this language.

One of the things that many people like about JavaScript is its versatility. This language allows you to use object-oriented programming, imperative, and even functional. And you can switch from one paradigm to another, depending on specific needs and preferences.
')
Although JavaScript supports functional programming techniques, it is not optimized to fully exploit this paradigm, like Haskell or Scala. It is not necessary to ensure that your JS programs fully comply with the concepts of functional programming. But their use helps to keep the code clean and concentrate on creating an architecture that is easily tested and can be used in several projects.

Filtering to limit datasets


With the advent of ES5, arrays in JS inherited several methods that make functional programming more convenient. Now arrays natively support map , reduce and filter . Each method passes through all elements of the array, and performs analysis without using cycles and changing local states. The result can be returned for immediate use, or left for later processing.

In this article we will look at the filtering procedure. It allows you to calculate each element of the array. Based on the transmitted test condition (test condition), it is determined whether to return a new array containing the results of the calculation. When using the filter method, you receive in response another array, of the same length or less than the initial one. It contains a subset of elements from the source array that satisfy the specified conditions.

Use a loop to demonstrate filtering.


An example of a problem that can be solved by filtering is limiting an array containing string values ​​to only those that consist of three characters. The task is not complicated, and it can be solved quite skillfully with the help of vanilla JS-cycles for, without using a filter. For example:

 var animals = ["cat","dog","fish"]; var threeLetterAnimals = []; for (let count = 0; count < animals.length; count++){ if (animals[count].length === 3) { threeLetterAnimals.push(animals[count]); } } console.log(threeLetterAnimals); // ["cat", "dog"] 

Defined an array containing three string values. Created an empty array to store only the string of three characters. Defined a counter variable for the for loop used as the array is iterated. Each time the loop finds a three-character string value, it places it in the second array. Upon completion, the result is logged.

Nothing prevents changing the source array in a loop. But if we do this, we will lose the original values. It is better to create a new array, but do not touch the original one.

Using the Filter Method


The previous solution is technically correct. But using the filter method makes the code much cleaner and simpler. For example:

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

Here we also started with a variable containing the source array. We defined a new variable for the array, where we will put the string of three characters. But using the filter method, we directly linked the filter results to the second array. Passing the filter an anonymous in-line function that returns true if the length of the operated value is three.

The filter method works like this: passes through each element of the array and applies a test function to it (test function). If the function returns true , the filter method returns an array containing this element. Other items are skipped.

The code is much cleaner. Even without knowing in advance what the filter does, you can understand its principle of operation from the code.

Clean code is one of the nicest by-products of functional programming. This is a consequence of the restriction of the transformation of external variables from functions and the need to store fewer local states. The variable count and the different states that the threeLetterAnimals array threeLetterAnimals when looping through the source array are additional states that need to be monitored. The filter method has saved us from the loop and count variable. And we do not change many times the value for the new array, as in the first case. We defined it once and associated it with the value resulting from applying the filter condition to the original array.

Other Filter Formatting Methods


You can write even shorter. We use const declarations and anonymous inline arrow functions. This is thanks to EcmaScript 6 (ES6), which is natively supported by most browsers and JavaScript engines.

 const animals = ["cat","dog","fish"]; const threeLetterAnimals = animals.filter(item => item.length === 3); console.log(threeLetterAnimals); // ["cat", "dog"] 

Perhaps most often it is better to avoid the old syntax, unless your code should match the already existing code base. But you need to approach this selectively. The more you think it over, the more difficult each line of code becomes.

JavaScript is attractive because it allows you to organize code in a variety of ways, reducing size, increasing efficiency, clarity and maintainability. But because of this, the development teams have to create general guidelines for the design of the code and discuss the advantages and disadvantages of each decision.

To make the code more readable and more flexible, you can do so. Take an anonymous built-in switch function, turn it into a traditional named function and pass it directly to the filter method. It might look like this:

 const animals = ["cat","dog","fish"]; function exactlyThree(word) { return word.length === 3; } const threeLetterAnimals = animals.filter(exactlyThree); console.log(threeLetterAnimals); // ["cat", "dog"] 

Here we simply extracted the anonymous built-in switch function defined before, and turned it into a separate named one. We have defined a pure function. It gets the corresponding value type for the elements of the array, and returns the same type. We can simply pass the name of this function to the filter as a condition.

Quick overview of Map and Reduce


Filtering works hand in hand with two other functional methods of ES5 - map and reduce . When creating chains of methods, you can use this combination to write very clean code that performs fairly complex functions.

Recall that the map method goes through each element of the array, converts it in accordance with the function, and returns a new array of the same length, but with transformed values.

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

The reduce method passes through an array and performs a series of operations. The intermediate result of each of them passes to the adder. Upon completion of array processing, the method gives the final result. In our case, you can use the second argument to initialize the adder to 0.

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

All three methods leave the original array intact, in accordance with the practice of functional programming.

Creating chains from Map, Reduce and Filter


Consider the simplest example. Suppose you need to take an array of string values, and return one consisting of three characters. But at the same time format it in the style of StudlyCaps . Without map , reduce and filter it will look something like this:

 const animals = ["cat","dog","fish"]; let threeLetterAnimalsArray = []; let threeLetterAnimals; let item; for (let count = 0; count < animals.length; count++){ item = animals[count]; if (item.length === 3) { item = item.charAt(0).toUpperCase() + item.slice(1); threeLetterAnimalsArray.push(item); } } threeLetterAnimals = threeLetterAnimalsArray.join(""); console.log(threeLetterAnimals); // "CatDog" 

Yes it works. But we created a bunch of unnecessary variables, and maintain the state of the array, which changes as it passes through different cycles. You can do better.

You can declare a target empty array using let or const .

Create pure functions that take and return string values. Then we use them in chains of the map , reduce and filter methods, passing the results from one to another:

 const animals = ["cat","dog","fish"]; function studlyCaps(words, word) { return words + word; } function exactlyThree(word) { return (word.length === 3); } function capitalize(word) { return word.charAt(0).toUpperCase() + word.slice(1); } const threeLetterAnimals = animals .filter(exactlyThree) .map(capitalize) .reduce(studlyCaps); console.log(threeLetterAnimals); // "CatDog" 

Three pure features: studlyCaps , exactlyThree and capitalize . You can pass them directly to map , reduce and filter within the same unbroken chain. First, using exactlyThree filter the source array. Pass the result to capitalize . And already its result is processed with the help of studlyCaps . The final result is assigned directly to the variable threeLetterAnimals . Without cycles and intermediate states, without touching the source array.

We got a very clear and easily tested code. Pure functions can be used in other contexts or transformed.

Filtration and performance


Do not forget that the filter method will probably work a little slower than the for loop until browsers and JS engines are optimized for new methods of working with arrays ( jsPerf ).

In any case, you can recommend using these methods instead of loops. A very slight drop in performance pays off with a cleaner code, convenient to maintain. And it is better to optimize for real situations, when it is really necessary to increase the speed of work. In most web applications, the filter method is unlikely to be a bottleneck. But the only way to make sure of this is to try it yourself.

If it turns out that the filter in a real situation is much slower than the cycle, if it affects the users, then you know where and how you can optimize. And as far as the JS engines are finished, the performance will only increase.

Do not be afraid to start using filtering. In ES5, this functionality is native and supported almost everywhere. Your code will be cleaner and easier to maintain. Thanks to the filter method, you will not change the state of the array as you calculate. Each time you return a new array, and the original will remain intact.

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


All Articles