Welcome to the chapter on arrays and objects. In this chapter we continue our journey of exploring higher order functions that are useful for arrays.
Arrays are used throughout our JavaScript programming world. We use them to store data, manipulate data, find data, and convert (project) the data to another format. In this chapter we are going to see how to improve all these activities using the functional programming techniques we have learned so far.
We create several functions on array, and we solve the common problems functionally rather than imperatively. The functions that we are creating in this chapter might or might not be defined already in the array or object prototype. Be advised that these are for understanding how the real functions themselves work, rather than overriding them.
Note
The chapter examples and library source code are in branch chap05. The repo’s URL is https://github.com/antoaravinth/functional-es8.git
Once you check out the code, please check out branch chap05:
...
git checkout -b chap05 origin/chap05
...
For running the codes, as before run:
...
npm run playground
...
Working Functionally on Arrays
In this section we create a set of useful functions, and using those functions we solve common problems with Array.
Note
All the functions that we create in this section are called projecting functions . Applying a function to an array and creating a new array or new set of value(s) is called a projection . The term will make sense when we see our first projecting function map.
map
We have already seen how to iterate over the Array using forEach . forEach is a higher order function that is going to iterate over the given array and call the passed function with the current index as its argument. forEach hides away the common problem of iteration, but we cannot use forEach in all cases.
Imagine we want to square all the contents of the array and get back the result in a new array. How can we achieve this using forEach? Using forEach we cannot return the data; instead it just executes the passed function. That’s where our first projecting function comes into the picture, and it’s called map.
forEach Function Definition
map Function Definition
and returning the results from the function. Now is a good time to talk about the term projecting function. We mentioned earlier that the map function is a projecting function . Why do we call the map function that? The reason is quite simple and straightforward: Because map returns the transformed value of the given function, we call it a projecting function. Some people do call map a transforming function, but we are going to stick to the term projection.
Wrapping Functions into arrayUtils Object
Note
In the text we call them map rather than arrayUtils.map for clarity purposes.
apressBooks Object Describing Book Details
Note
This array does contain real titles that are published by Apress, but the review key values are my own interpretations.
All the functions that we are going to create in this chapter will be run for the given array of objects. Now suppose we need to get the array of an object that only has a title and author name in it. How are we going to achieve the same thing using the map function? Do you see a solution in your mind?
We do not always just want to transform all our array contents into a new one. Rather, we want to filter the content of the array and then perform the transformation. It is time now to meet the next function in the queue, filter.
filter
Imagine we want to get the list of books with ratings higher than 4.5. How we are going to achieve this? It is definitely not a problem for map to solve, but we need a function similar to map that just checks a condition, before pushing the results into the results array.
filter Function Definition
We are constantly improving the method to deal with arrays using these higher order functions. Before we go further with the next functions on the array, we are going to see how to chain the projection functions (map, filter) to get the desired results in complex situations.
Chaining Operations
This code literally tells the problem we are solving: “Map over the filtered array whose rating is 4.5 and return their title and author keys in an object.” Due to the nature of both map and filter, we have abstracted away the details of the array themselves, and we started focusing on the problem at hand.
We show examples of chaining methods in the upcoming sections.
Note
We will see another way to achieve the same thing via function composition later.
concatAll
Updated apressBooks Object with Book Details
As you can see, the return data from our map function contains Array inside Array because our bookDetails itself is an array. Now if we pass these data to our filter, we are going to have problems, as filters cannot work on nested arrays.
concatAll Function Definition
Here we just pushed up the inner array while iterating into our results array.
Note
We have used JavaScript Function’s apply method to set the push context to results itself and pass the argument as the current index of the iteration - value.
We have seen how designing a higher order function within the world of the array does solve a lot of problems in elegant fashion. We have done a really good job up to now. We still have to see a few more functions with respect to arrays in the upcoming sections.
Reducing Function
If you talk about functional programming anywhere, you often hear the term reduce functions. What are they? Why they are so useful? reduce is a beautiful function that is designed to showcase the power of closure in JavaScript. In this section, we are going to explore the usefulness of reducing an array.
reduce Function
With this problem, we are reducing the array (which has several data) into a single value. We start with a simple accumulator ; in this case we call it as result to store our summation result while traversing the array itself. Note that we have set the result value to default 0 in case of summation. What if we need to find the product of all the elements in the given array? In that case we will be setting the result value to 1. This whole process of setting up the accumulator and traversing the array (remembering the previous value of accumulator) to produce a single element is called reducing an array.
reduce Function First Implementation
reduce Function Final Implementation
We have made the changes to the reduce function so that now if initialValue is not passed, the reduce function will take the first element in the array as its accumulator value.
Note
Have a look at the two for loop statements. When initialValue is undefined, we need to start looping the array from the second element, as the first value of the accumulator will be used as the initial value. If initialValue is passed by the caller, then we need to iterate the full array.
Because we are done iterating all our array content, the latest accumulator value will be returned, which is the result.
As you can see here, in this process we have abstracted away internal details into higher order functions, leading to elegant code. Before we close this chapter, let’s implement the zip function, which is another useful function.
Zipping Arrays
Splitting the apressBooks Object
reviewDetails Object Contains Review Details of the Book
In Listing 5-11, the reviews are fleshed out into a separate array; they are matched with the book id. It’s a typical example of how data are segregated into different parts. How do we work with these sorts of split data?
zip Function
The task of the zip function is to merge two given arrays. In our example, we need to merge both apressBooks and reviewDetails into a single array, so that we have all necessary data under a single tree.
zip Function Definition
Once you get the minimum length, we call our passed higher order function fn with the current leftArr value and rightArr value.
Now clone gets a copy of what’s there in the book object. However, the important point to note is that clone is pointing to a separate reference. Adding or manipulating clone doesn’t change the real book reference itself. In JavaScript, objects are used by reference, so changing the book object by default within our zip function will affect the contents of bookDetails itself, which we don’t want to do.
Finally, we are returning it. Now you can apply the reduce function as before to solve the problem. zip is yet another small and simple function, but its uses are very powerful.
Summary
We have made a lot of progress in this chapter. We created several useful functions such as map, filter, concatAll, reduce, and zip to make it easier to work with arrays. We term these functions projection functions, as these functions always return the array after applying the transformation (which is passed via a higher order function). An important point to keep in mind is that these are just higher order functions, which we will be using in daily tasks. Understanding how these functions work helps us to think in more functional terms, but our functional journey is not yet over.
Having created many useful functions on arrays in this chapter, in the next one we will be discussing the concepts of currying and partial application. These terms are nothing to fear; they are simple concepts but become very powerful when put into action. See you in Chapter 6.