Yesterday when we were on our way home, my girlfriend pointed out the window and said, "Look, look there in the background." At first I couldn't see what she was talking about, but then I spotted them: the amazing Bernese Alps on full display in the background. I quickly snapped this shot with my phone, which you can see in the picture above. Really nice!
Amazing Bernese Alps in the Background
Yesterday when we were on our way home, my girlfriend pointed out the window and said, "Look, look there in the background." At first I couldn't see what she was talking about, but then I spotted them: the amazing Bernese Alps on full display in the background. I quickly snapped this shot with my phone, which you can see in the picture above. Really nice!
Map, Filter and Reduce in JS
Normally I don't write about programming topics on this blog, since I dedicate it more to pictures, maps, and charts, but a couple of days ago I was explaining to a colleague at work some parts of an internal tool in which we use React and functional programming.
And it is true that if you haven't had experience with JavaScript's higher-order functions map, filter & reduce, they might appear daunting at the beginning. But once you get used to them, you never look back! They are extremely useful for manipulating arrays and arrays of objects.
So the objective of this post is to explain all three of them using a simple example.
Data
Let’s create a quick array of objects with the top five movies of all time according to IMDb ratings:
const top5Movies = [
{
"title": "The Shawshank Redemption",
"rank": "1",
"id": "tt0111161",
"imdbrating": "9.3",
"year": "1994",
"genre": "Drama",
"director": "Frank Darabont",
"actors": "Tim Robbins, Morgan Freeman, Bob Gunton",
"language": "English",
"country": "United States"
},
{
"title": "The Godfather",
"rank": "2",
"id": "tt0068646",
"imdbrating": "9.2",
"year": "1972",
"genre": "Crime, Drama",
"director": "Francis Ford Coppola",
"actors": "Marlon Brando, Al Pacino, James Caan",
"language": "English, Italian, Latin",
"country": "United States"
},
{
"title": "The Godfather: Part II",
"rank": "3",
"id": "tt0071562",
"imdbrating": "9.0",
"year": "1974",
"genre": "Crime, Drama",
"director": "Francis Ford Coppola, Mario Puzzo",
"actors": "Al Pacino, Robert De Niro, Robert Duvall",
"language": "English, Italian, Spanish, Latin, Sicilian",
"country": "United States"
},
{
"title": "Pulp Fiction",
"rank": "4",
"id": "tt0110912",
"imdbrating": "8.9",
"year": "1994",
"genre": "Crime, Drama",
"director": "Quentin Tarantino",
"actors": "John Travolta, Uma Thurman, Samuel L. Jackson",
"language": "English, Spanish, French",
"country": "United States"
},
{
"title": "The Good, the Bad and the Ugly",
"rank": "5",
"id": "tt0060196",
"imdbrating": "8.8",
"year": "1966",
"genre": "Adveture, Western",
"director": "Sergio Leone",
"actors": "Clint Eastwood, Eli Wallach, Lee Van Cleef",
"language": "Italian, English",
"country": "Italy, Spain, West Germany"
}
]
Map
Let’s say a friend says to you, ah, great dataset, but could you give me a list with just the titles of those movies? In this case map is your friend :). This method loops over an array and runs a function on each element of the array, returning the modified value as a new element of a new array.
In our case, we want to loop over this top5Movies array and just extract the titles:
const titles = top5Movies.map(movie => movie.title)
So with this simple statement we loop over each “movie” item (an object), apply a simple function (with arrow notation) that returns the title (implicit return), and assign the results to this new titles array. If we look at the contents of this array, our titles should be there:
> titles
[
'The Shawshank Redemption',
'The Godfather',
'The Godfather: Part II',
'Pulp Fiction',
'The Good, the Bad and the Ugly'
]
If you want to better understand the concept, think about it without arrow notation:
top5Movies.map(
function (movie) {
return movie.title
}
)
As you can see, you are applying a function on each iteration and returning the value.
Please also note that the map method will always create a new array without mutating the original one.
Filter
Now let’s say another friend asks you if you can give them a copy of this array of objects, but including only the movies in which Spanish is spoken. This is where the filter operation can be quite useful. This method iterates over the source array, but instead of returning a modified copy of your array element, it just tests the condition defined in the function and returns a copy of the original element if the condition is met.
So, for this request, we will check if the field “language” includes the string “Spanish”:
const topMoviesWithSpanish = top5Movies.filter(
movie => movie.language.includes("Spanish"))
The newly created topMoviesWithSpanish array will only include:
[
{
"title": "The Godfather: Part II",
"rank": "3",
"id": "tt0071562",
"imdbrating": "9.0",
"year": "1974",
"genre": "Crime, Drama",
"director": "Francis Ford Coppola, Mario Puzzo",
"actors": "Al Pacino, Robert De Niro, Robert Duvall",
"language": "English, Italian, Spanish, Latin, Sicilian",
"country": "United States"
},
{
"title": "Pulp Fiction",
"rank": "4",
"id": "tt0110912",
"imdbrating": "8.9",
"year": "1994",
"genre": "Crime, Drama",
"director": "Quentin Tarantino",
"actors": "John Travolta, Uma Thurman, Samuel L. Jackson",
"language": "English, Spanish, French",
"country": "United States"
}
]
Since those are the movies that include “Spanish” in the language field. As was the case with map, filter will create a new copy of the array so the original is not mutated.
Reduce
Finally, another friend asks you if it is possible to get the average rating of those top 5 movies. So you say sure, take the map function you already learned, and tell them: here are the ratings:
> top5Movies.map(
movie => parseFloat(movie.imdbrating))
[ 9.3, 9.2, 9, 8.9, 8.8 ]
And your friend says: well, thank you, but what I asked for is the “average” of those.
You start to think about it 🤔, and realise you need to iterate over each rating but instead of changing it, you need to add it to the next one and so on, and once you have the final sum, you can divide it by the number of ratings.
This process of “aggregating” values is exactly what the reduce method does; it iterates over each element and applies a function you define with the item and the aggregator.
Let’s take those ratings and aggregate (sum) them together:
> [ 9.3, 9.2, 9, 8.9, 8.8 ].reduce(
(aggregator, rating) => (aggregator + rating))
45.2
As you can see, your ratings array was “reduced” to a single value that’s the sum of the ratings.
Using this principle, we can chain our map function that extracts the rating with this reduce one, and by dividing by the number of items, we can give our friend the average:
> top5Movies.map(
movie => parseFloat(movie.imdbrating)).reduce(
(aggregator, rating) => (
aggregator + rating)) / top5Movies.length;
9.040000000000001
Now, one of the most powerful features of this reduce higher-order function is that it allows you to create an object from an array.
In that case, the reduce function takes two arguments: a starting value and a function. The starting value is the initial value of the accumulator, which is the object that will be created. The function is used to iterate over the array and add each element to the accumulator. The function takes two arguments: the accumulator and the current element.
So let’s go back to our movies example. Let’s say we want to convert that top5Movies array into an object, keyed by the IMDb id, so it’s easier to get a movie based on this id.
So let’s do it in two steps. First, let’s create the template that we will use for writing our reduce function:
top5Movies.reduce((acc, item) => { return acc}, {})
Obviously this returns an empty object, because it’s iterating over our top5Movies but always returning the empty accumulator (acc) that we initialized at the end of this reduce function ({}). But if now we just add:
top5Movies.reduce((acc, item) => { acc[item.id] = item; return acc}, {})
Now the accumulator will gradually add the movie keyed by its id and at the end we get what we want:
{
tt0111161: {
title: 'The Shawshank Redemption',
rank: '1',
id: 'tt0111161',
imdbrating: '9.3',
year: '1994',
genre: 'Drama',
director: 'Frank Darabont',
actors: 'Tim Robbins, Morgan Freeman, Bob Gunton',
language: 'English',
country: 'United States'
},
tt0068646: {
title: 'The Godfather',
rank: '2',
....
And that’s it! I hope this post will help you better understand the map, filter & reduce higher-order functions in JavaScript.
Heatwave in Europe
I recently had the pleasure of visiting Cubelles, a beautiful Catalan coastal town near Barcelona. I hit the jackpot when I found an apartment right on the beach! My time there was pure bliss; I spent almost every day soaking up the sea and cooling off in the waves.
But every time I watch the news, I realize how lucky I was to spend my days there during the heat wave that is hitting Europe. There are raging fires in many places in southern Europe, and places where the summer is not usually that hot, like the UK, are experiencing record temperatures.
No comments. The climate change problem is already here :/
Back in Berlin
A few days ago I had the opportunity to visit Berlin again, and I spent some very pleasant days in the company of my dear friend C. In those days I remembered why the German capital is one of my favorite cities in Europe, and although I am satisfied with my life at the moment, I will always miss that very interesting place!
One of the things I had always been curious about since leaving Berlin was what had happened with the famous Humboldt Forum. I remember the first time I visited that space in 2015: at that time it was a temporary construction, created by the Humboldt foundation, that served as an exhibition space and viewing platform for the Berlin Palace - Humboldt Forum reconstruction project.
Well, 7 years later, this project is finished, the palace is back to its former glory (at least the exterior facade), and it has some interesting exhibitions inside. Above you can find a picture of how it looks nowadays. Bis (hoffentlich) bald, Berlin!
Next page