This article also exists in: Italian

You won’t be able to do without it

In the life of a developer you may have had to deal with them (or rather YOU SHOULD), the Holy Trinity of magical methods on arrays: but if you have never study in deep, i suggest you read and above all try (even using the simple console browser - F12) these functions…

Functions that I remember by now are standard in the JS ecosystem (ES5, A.D. 2009) but which are also present in most of the other programming languages more purely backend (Python, PHP, etc…)

Functions that will allow you to manipulate arrays by writing less code, and most importantly by writing testable, scalable and maintainable code.

Higher order functions… but we’ll talk about that later!

Usefull methods with array

You will find many examples with arrays of numerical values on the net 🤘 … Let’s try to use, instead, an array of objects that represent professionals with their work items: let’s see what comes out!

const data = [
    { object: '🧰', worker: '👨‍🔧', expertise: 1 },
    { object: '✈️', worker: '👨‍✈️', expertise: 7 },
    { object: '🍳', worker: '👩‍🍳', expertise: 10 }
];

MAP

Suppose we want to obtain, from all professional figures, the action they perform using their work items (object) and their professional figure (worker). To do this, analytically, we should cycle for each object in the array and based on the type of object (or worker, or both and this is a logical decision depending on the domain and scope of use) to choose the action that can be performed.

We are helped by map()

Simply map accepts a function as an argument which is applied to each element of the calling array: the result will be added to the result array of the map method itself.

It is easier to do than to explain! 🤣

const startWorking = (obj) => {  //implementazione SOLO DI ESEMPIO...
   if(obj.object === "🧰") {
     return '🚙';
   }
   if(obj.object === "✈️") {
     return '🛫';
   }
   if(obj.object === "🍳") {
     return '🍲';
   }
};

let resultMap = data.map(startWorking);

// resultMap = ["🚙", "🛫", "🍲"]
Ref: map() | map()

REDUCE

In this case we want to obtain all the years of experience of our professional figures. Easy scope: counter sum, cycle for each element of the array and increment the counter. Why not use reduce() instead?

Reduce() applies a reducer function on each element of the array: supplying an initial value the accumulator (first argument of the reducer) takes this value, otherwise the first value of the array is taken. The value returned by the reducer is assigned and stored in the accumulator through each iteration of the entire array: this value will then be the final value of the function.

NB: if initialValue is not supplied, the reducer will start at index 1 of the array, if given, it will start at index 0.

const yearExpertise = (accumulator, work) => accumulator + work.expertise;

let sum = data.reduce(yearExpertise, 0);
// sum = 18

let sum2 = data.reduce(yearExpertise, 5);
// sum = 23
Ref: reduce() | reduce()

FILTER

We now want to obtain, among all the professional figures, those that have to do with the kitchen. The good filter() comes in handy.

Filter() applies a filter function to all elements of the array: only those that pass the test implemented in the filter function will be present in the resulting array.

const inKitchen = (work) => { //implementazione SOLO DI ESEMPIO...
  return ["🍳","🔪", "🍴"].indexOf(work.object) > -1 ? true : false;
}

let worksInKitchen = data.filter(inKitchen);

// worksInKitchen = [ { object: '🍳', worker: '👩‍🍳', expertise: 10 } ];
Ref: filter() | filter()

LET’S COMBINE THE METHODS

Due to the fact that all the methods present are called on array and that map and filter return array, we can combine the methods to be able to create clean, clear and easy data manipulations testable.

We want, for example, to obtain the total years of experience of all the non-cooking expertise.

  1. First we filter the data looking for NON cooking expertise
  2. We map the resulting array from point 1. to create an array of years-experience (array of numbers)
  3. We use reduce() to get the total years of experience

PS: of course points 2. and 3. could be implemented simply with reduce() on the array of point 1.

const notInKitchen = (work) => { //implementazione SOLO DI ESEMPIO...
  return ["🍳","🔪", "🍴"].indexOf(work.object) > -1 ? false : true;
}
const yearExpertise = (accumulator, expertise) => accumulator + expertise;

let expertiseNotKitchen = data.filter(notInKitchen)
                         .map((work) => work.expertise)
                         .reduce(yearExpertise, 0);

// expertiseNotKitchen = 8;

What do I like about these 3 methods? Simple. I say this in three words: higher order functions.

map(), filter(), reduce() are all three higher-order functions. This means that they are functions that take functions as parameters (or that return functions as a result, but this is not the case).

All this translates into the possibility of migrating code, written in the old procedural style, in a functional style. The whole flow of execution is composed of the execution of functions, functions that take data as input and return data (and or functions in turn).

Being able to do this the resulting code, as in the example above, is more readable, more talking (no need for documentation… well-written code is self-speaking) and testable: using unit tests on the callback functions (e.g. notInKitchen or yearExpertise), which are pure functions, we have robust and scalable code without affecting the integrity of the final result.

About the author

For the last 15 years, Stefano Frasca has worked with a variety of web technologies both backend and frontend. He is currently focused on front-end development. On his day to day job, he is working as a FullStack developer & Frontend specialist at Maestrale IT. He has worked remotely for years, passionate about photography, food and code 😎
Do you want to know more? Visit my website!