Filter, Map and Reduce Operations
Performant, fast and practical code development with Filter, Map and Reduce operations.
I always compare these operations to the preparation of a meal. This way it becomes more memorable. I'll get into the subject quickly, let me start by telling you how melemen is coded.
To make melemen, we will use many ingredients in our kitchen in certain proportions. So, which materials will we use? The arrayFilter function does this process, that is, it extracts the ones we need from all the data we have. So, the filter function is needed to find the salt, oil, tomato, pepper and egg we will use for melemen. Unfiltered data may contain things we don't want, just like our kitchen. We now have the materials. So, do we put them in a pot and wait for them to cook? No, we definitely need to chop the tomatoes and peppers and break the egg. For this we need to use the arrayMap function. Map operation processes data and converts it to another type or result. In other words, operations such as chopping tomatoes belong to the map function. If we think of this as data, we can say that the VAT amount we will find as a result of multiplying the VAT rate and the pure amount. In fact, the VAT amount is not a value but a result. Or data processed within the row, such as the net total of the row, should be processed with a map. Yes, we have now processed the ingredients from our ingredients, that is, we cut the tomatoes and peppers and broke the egg. Now it's time for the cooking part. Reduce is the main issue to pay attention to here. Because the result obtained as a result of the reduce operation no longer leads to any of the initial results. So, when you cook melemen, we no longer call it the cooked version of tomatoes, peppers, eggs, oil and salt, we call it directly melemen. Reduce produces a unique result on a data string. For example, we can say the sum of invoice lines. Sometimes it can also be grouped, for example totals according to VAT rates. Here, even if more than one data is created as a result of grouping, it is not possible to return to the main data as a result, a new data is created. I am not very good at recipes, if I were, I would publish a cookbook, because it would be a better job than this software :)
Preliminary information for functions starting with List and Array
If you see functions with list and array prefixes that have the same function postfix, choose the ones that start with array. For example, the arrayFilter and listFilter functions perform filtering on arrays and lists. But actually list is a virtual array. Each time, the list is converted to an array, the operation is performed, and then the list is converted again. For this reason, even if the result will be used as a list, we must carry out the operation through an array, and if our initial value is list, we must convert it to array with listToArray, and after all operations are completed, it must be converted back to a list. It is necessary to convert lists to arrays every time and prevent them from being converted to lists again. That's why our topic will be explained through array prefixed functions.
arrayFilter(array, function(element [,index [,array]])) array
Filter function allows processing by sending all the elements in the array one by one to the function given in the second parameter. The elements of the array are passed to the function through the element variable, the sequence number through the index, and the main index through the array variable. At least the element variable must be specified in the function to be written, other variables do not need to be specified.
It is generally filtered by filtering the elements on an array. For example, it finds invoices with the customer name "Halit". For this, if we want to select records with the customer field "Halit" from an array called invoices, we can use it as follows.
The point to be noted is that all functions where the result is returned as boolean can work for this operation. In other words, it is used in more complex operations such as operations performed in a function block and the result exceeding a certain value. However, the point we should not forget is that if the value to be calculated and controlled in the filter can be used later, it would be appropriate to first calculate the data by mapping and then reduce it to the simple filter level. But if only the calculation result will be filtered, making a map may mean producing extra data. I would like to draw your attention to the fact that we did not use the index and main array again in this example.
arrayMap(array, function(element [,index [,array]])) array
Map function creates a new value by sending all the elements in the array one by one to the function given in the second parameter. The elements of the array are passed to the function through the element variable, the sequence number through the index, and the main index through the array variable. At least the element variable must be specified in the function to be written, other variables do not need to be specified. The new value created here is added to the result array with the same index. The element to be added may be a new variable or a modified version of the existing variable.
It is generally used for operations where the result is obtained by calculation. We can use it in operations such as finding the VAT amount by multiplying the total row total by the VAT rate, as in the example I gave before. For example, let's have total and vat fields in an array and calculate and add the vat_amount field to this line.
In this example, the vat_amount field was calculated for each element in the bill_satirlar array, added to the existing struct, and the existing and added struct was converted as a new value. The result was transferred to the same variable, which was done because the same variable was modified (that is, the existing values were not corrupted). It is reasonable to do the transaction this way. However, if you are doing something that will make the elements in the array different from the old ones, assigning them to the old array variable may be a wrong move. For example, we can convert the TL values in the array into USD and import them into a new array.
arrayReduce(array, function(result, element [, index [, array]]) [,initial_value]) any
Reduce function sends each element in the array to the function, assigns it to the result value and returns the result value as a result. Although this process is a bit confusing, we can actually compare it to a system where we add the result of each line to the result value and call the function again by giving the last result value.
It is generally used in operations such as subtotal or grouping total. For example, let's find the total of an invoice and the VAT totals according to the VAT rate.
In this example, both the plain logic total and the grouping logic total are taken. While the final result is reached by adding the result value each time in the plain sum, in the process performed by grouping, another struct is created and returned. The point to be considered here is to set the result value with the initial value before starting the operations, since initial_value does not always work stable.
Pipe operations
This concept is used for operations that try to produce results by working sequentially and intertwined. For example, let's take the general VAT amount total of the non-promotional lines within the invoice lines.
You can call the filter map and reduce operation infinite times due to the intertwined need, filter, calculate and filter again according to the calculated field, create a grouped result, calculate, filter this result and turn it into a result again. So, with this intertwined trio, you can quickly change the codes to whatever you want. Of course, you do not have to use all three, you can meet your needs with just a binary process such as filter and map, or you can take hierarchical data to higher levels by calculating it, such as map and reduce.
Appropriate approach
Sir, you can say that we can calculate these operations, for example, while performing loop operations in tables, that is, printing the fields on the screen. However, the important point is to distinguish between the model and the view.
The purpose of the three important functions I have given in this topic and the other functions I have not given is to shape the model. In other words, other software developers will clearly understand what the pipe operations you perform on the model and the calculations and results you make in the software developed by the community will be at the model level, and they will know that there are no aggregate operations hidden among the HTML and other codes. If you can finish the process before outputting, when a new feature is added, you either open another node on the pipe or assign the necessary values to a new variable. Transactions in the traditional loop process, referred to as spaghetti, result in codes that are difficult to understand, full of overlooked calculations, or risky and difficult to break into. The clean code approach ensures that the model and view codes are isolated from each other and grouped as much as possible, even if they are in the same file. In addition, these functions minimize the code and eliminate the crowd of intermediate variables that will occur in the spaghetti process. I will explain these functions, which can overcome some restrictions in the concept of query and work intertwined with query of query, in detail in my article that I will publish later.
Written by: Halit Yurttaş - Workcube Software Architecture