We could seperate the methods that work with arrays into two categories: mutating and not mutating. You can check out this MDN article about array methods. The Mutators mutate the Array and the Accessors and the Iterators do not, they return a new array every time you call them.
In functional programming mutating data is considered evil. Not mutating data makes your code less error prone. You’re always sure that you are working with the array or object you expect, without having to worry about it being mutated somewhere else. So let’s do that!
When you’re writing logic dealing with arrays, these 3 are you’re friends. Almost all of the array related logic can be written using these three methods. Let’s do an example. I have an array of users, and I want to change the age John to 23. We can use
map for that!
So here we call
map on the ‘users’ array. Map loops through all the items in the array and allows you to return a new “version” for each item. As you can see we check if we are at the user we want to change (in this case John) and then we return a new version of John where he is 23 (using ES6 object spread). If the user is not the one we’re interested in, we just return the old user.
An important thing to notice is that we do not change the
users array. Instead we create a new array and store it in
newUsers. This is the essence of immutability.
In this article we I use ES6 examples. However map, filter and reduce are perfectly fine in good old ES5. They are supported by all major browsers plus IE 9 and up.
Now let’s try something else, in this example, we want an array with only the the female users. We can use
filter for that!
Wow, that looks easy! Filter loops through all the items in an array and let’s your return
false for each item. If it’s true, keep the item, if it’s false, remove it. Because we return
user.gender === 'f', only a female user will return
true, thus we only keep those in the list.
So let’s say I want to convert an array into a key value pair object. For some real time data storage techniques this is can be convenient. This is a case where
reduce comes in handy. Reduce turns an array into something else, that can be a simple value, another array or in our case, an object.
Reduce loops through an array, allowing you to construct a final value, by doing something with it for every item. It takes a callback where the first parameter is the previously returned value (here called
usersObject). The second one is the current item you’re at. We add every item to the
usersObject with it’s id as the key, returning the new
usersObject for the next one to use and so forth.
But what is the previous value for the first item? Well that’s why reduce takes a second parameter, the initial value. For this one we start with an empty object
Now let’s do a combined example. Let’s say we want the total age of all females in the list. We can combine
reduce to do this!
totalFemaleAge will now equal 76 :). So what did we do? First we got a list of all female users by using
filter. Then we called
reduce on that list.
Reduce might seem a bit confusing at first, but when you start to fiddle around with it you’ll get comfortable using it.
Map, filter and reduce (and combinations of them) can provide almost all of your array needs. But what about pushing stuff into the array? And sorting? Well
push is a mutating method and we don’t want that. How do we add items to an array without mutating it? In ES6 we can use the neat array spread operator:
Here we create a new array, add all the items of
users to it, plus the
newUser. If you’re not using ES6, I would suggest using
concat on a new array, to avoid mutating the old one:
slice returns a shallow copy of (a part of) an array. If you omit the parameters it will just return a copy of the whole array. Then we sort on that copy, to make sure we do not mutate the old one. I do agree that it is a bit hacky but it’s simple and it works.
forEach loops through an array and let’s you do “something” with each item, returning nothing in the end. Keep in mind that most cases where you think you need
forEach, you actually need
reduce, especially if you’re not into the “functional” mindset yet.
forEach is valid in cases where you want to do a certain “side effect” for each item. For instance, sending a notification to each user:
But be careful with side effects, they can be hard to test and debug.
The downside of both is that in the case of a web application, is that they add extra weight to your application. Lodash is pretty small, but Immutable is notably big. Try to build your app without them (but ofcourse try them out) and use them when you feel the need to.