In a lecture about npm modules, I told you that you shouldn’t try to reinvent the wheel. First look for a ready-made solution, and only if you can’t find anything useful, then get down to business yourself.

But very often, even installing the npm module will be superfluous, because JavaScript already has many useful functions for working with data.

— For example?

— For example, one of the most common data types in JS is an array. All arrays already contain all methods (functions) to work with the data inside the array.

— I don’t understand how an array can contain functions. It seemed to me that arrays consist of ordinary values, such as numbers or lines.

— Excellent remark. Let’s start with an example of a simple array of a few numbers.

const arr = [ -1, 2, 4, 0, -5, 7 ];

In the line above, I created an array arr. Imagine that you need to create a new array from all the elements of the array arr whose value is greater than zero. How would you do it?

— I would create an empty positives array, then iterate over the arr array with a for loop and put every matching array element in positives using the push function.

— Great plan. Pay attention to the push function. This is one of the “built-in” functions of all JS arrays. Even if there are no elements in the array, you can always add a new one using push.

Let’s try to write your algorithm in JS.

const arr = [ -1, 2, 4, 0, -5, 7 ];
const positives = [];

for (let i = 0; i < arr.length; i++) {
  if (arr[i] > 0) {
    positives.push(arr[i]);
  }
}

console.log(positives); // [ 2, 4, 7 ]

It turned out well, but it could be better.

Filter array elements

The filter function will help you filter out the unwanted elements from the array and return a new array consisting only of suitable items.

To work correctly, the filter function accepts a parameter called “filtering function” or “filtering predicate”. This “filtering function” takes a single element of the array and returns true if the element passes the filter, otherwise, it should return false.

Let’s rewrite the previous example using the filter function:

const arr = [ -1, 2, 4, 0, -5, 7 ];
const greaterThanZero = item => item > 0;

const positives = arr.filter(greaterThanZero);

console.log(positives); // [ 2, 4, 7 ]

This is not the shortest option, however. I created the greaterThanZero function just to emphasize that the filter function takes another function as a parameter.

In practice, for simple filters, you will more often see an anonymous filtering function written right inside the filter brackets.

const arr = [ -1, 2, 4, 0, -5, 7 ];

const positives = arr.filter(item => item > 0);

console.log(positives); // [ 2, 4, 7 ]

— Can I ask a stupid question?

— Sure.

— What happens if my “filtering function” always returns false?

— If the function that you use in Array.filter always returns false, then you’ll get an empty array as an output.

const arr = [ -1, 2, 4, 0, -5, 7 ];

const positives = arr.filter(item => false);

console.log(positives); // []
console.log(arr); // [ -1, 2, 4, 0, -5, 7 ]

Note that you will get a new empty array, and the original arr array will not change in any way.

— And if I choose to always return true, then I will get a copy of the input array?

— Right. But it’s like hammering nails with a microscope, so I do not recommend using the filter method for copying arrays.

With filter, you can also work with an array of objects. For example, you might want to filter all users over 18 years of age.

const users = [
  {name: 'Jake', age: 15},
  {name: 'John', age: 16},
  {name: 'Jill', age: 25},
  {name: 'Bill', age: 82},
];

const grownUps = users.filter(user => user.age > 18);

console.log(grownUps); // [ { name: 'Jill', age: 25 }, { name: 'Bill', age: 82 } ]

Iterate over array elements with forEach

The next standard task is to iterate over all the elements of the array. It can also be solved with the regular for loop.

Imagine that you need to display the value of all elements of an array, each on a new line.

const arr = [ -1, 2, 4, 0, -5, 7 ];

for (let i = 0; i < arr.length; i++) {
  console.log(arr[i]);
}

Using a regular for loop, you call the console.log function on each iteration of the loop and pass the current array element arr[i] in it.

The forEach function will allow you to shorten your code a bit. It is similar to the filter function in a way as you’ll also need a handler function to use it. This function will be called for each element of the array.

The fundamental difference is that forEach does not return anything.

const arr = [ -1, 2, 4, 0, -5, 7 ];

arr.forEach(item => console.log(item));

— Now let’s think about what we could make the example above even shorter?

— Hmm, I do not know. It seems to be quite short already.

— Look, the item => console.log(item) function does nothing with the item parameter other than passing it further into console.log which outputs item to the screen.

It turns out that we could use the console.log function without this wrapper from item => ... and the result would remain the same.

const arr = [ -1, 2, 4, 0, -5, 7 ];

arr.forEach(console.log);

Note that we don’t put parentheses after console.log because we don’t call the console.log function. We pass it as a parameter to the forEach loop, where it will be applied to each element of the array.

The call will be controlled by forEach, not by us.

This is a very important point at which many interns stumble.

Loop through array elements with map

The biggest drawback of the forEach loop is that it doesn’t return anything useful. If we want to get an array doubleArr where each element is equal to the element arr multiplied by two, then forEach won’t help.

To get an array equal in length to the initial array, but with modified elements, use the map function.

const arr = [ -1, 2, 4, 0, -5, 7 ];
const doubleArr = arr.map(item => item * 2);

console.log(arr); // [ -1, 2, 4, 0, -5, 7 ]
console.log(doubleArr); // [ -2, 4, 8, 0, -10, 14 ]

Note that the array arr has not changed in any way.

Together with filter, the map method is one of the most used array methods in JS.

Both filter and map return an array, so you could combine them into chains.

Let’s go back to the user example. The task is to create an array with the names of all users whose age is less than 18 years.

Let’s first apply the filter function to throw away all the underage users and then use map to transform an array of objects to an array of strings.

const users = [
  { name: 'Jake', age: 15 },
  { name: 'John', age: 16 },
  { name: 'Jill', age: 25 },
  { name: 'Bill', age: 82 },
];

const kidsNames = users
  .filter(user => user.age < 18)
  .map(user => user.name);

console.log(kidsNames); // [ 'Jake', 'John' ]

It will become very easy for you to read such code after you get used to the syntax.

The task of creating a new array from an existing one arises so often that the forEach loop can safely be called the most useless method in JavaScript. It’s almost always better to use map or a regular for loop instead.

Reduce an array to a single value

The reduce function is the most complex of the standard JavaScript array functions. With reduce, you can compress (reduce) an array to one element.

For the reduce function to work properly, you’ll need 2 parameters — the reducer function and the initial value.

The reducer function, unlike the functions you will use in map and filter, takes two parameters. The first is the accumulator. The second is the current element of the array.

Let’s see how we can calculate the sum of all elements of an array using reduce.

const arr = [ -1, 2, 4, 0, -5, 7 ];
const sum = arr.reduce((prev, cur) => prev + cur, 0);

console.log(sum); // 7

To make it a little clearer for you, I will rewrite this example using the for loop.

const arr = [ -1, 2, 4, 0, -5, 7 ];
let prev = 0; // accumulator

for (let i = 0; i < arr.length; i++) {
  prev = prev + arr[i]; // arr[i] is the current element of the array, same as cur in the reduce example
}

const sum = prev;

console.log(sum); // 7

— This is clearer, but it looks like there is at least one extra line.

— It’s good that you pay attention to the extra code. The assignment sum = prev makes the for loop example as similar as possible to the previous one with reduce.

— Can you elaborate on the “initial value” that we pass as the second parameter?

— When the reduce function starts, it only has the first element of the array, so you need to add an initial value to start the calculations. Most often, this is an empty string, null, or an empty object.

Let’s see what happens if you try to reduce on an array of objects and don’t add an initial value.

Here’s an example of how you could calculate the total salary for the year of all employees of the company.

const employees = [
  { name: 'John Richards', salary: 155000 },
  { name: 'Lenny Mingles', salary: 75000 },
  { name: 'Travis Markham', salary: 102000 },
  { name: 'Bertha Jackson', salary: 47000 },
  { name: 'Vasya Pupkin', salary: 322000 },
];

const salariesSum = employees
  .reduce((prev, cur) => prev + cur.salary);

console.log(salariesSum); // [object Object]7500010200047000322000

As a result, a weird strange string [object Object]7500010200047000322000 was displayed in the console, due to the fact that we do not added the initial value of the accumulator.

Let’s add one console.log to better understand what prev is on each iteration:

const salariesSum = employees
  .reduce((prev, cur) => {
    console.log(prev); // added for debugging
    return prev + cur.salary
  });

New lines will appear in the console.

{name: 'John Richards', salary: 155000}
[object object]75000
[object object]75000102000
[object object]7500010200047000
[object object]7500010200047000322000

Here’s what happened:

  1. We did not specify the initial value of the accumulator and therefore the first element was used as the accumulator the employees array.
  2. By default, when trying to add an object to a number, both were converted to a string.
  3. On the second iteration in prev string [object Object]75000 which will stick together with numbers until it ends array.
  4. Eventually we will end up with the string [object Object]7500010200047000322000.

— It doesn’t look like the sum of salaries of all employees.

— You are right. Does not look like it. Fortunately, fixing the problem is very easy.

Let’s add the initial value of the accumulator and make sure that the salary is calculated correctly.

const employees = [
  {name: 'John Richards', salary: 155000},
  { name: 'Lenny Mingles', salary: 75000 },
  { name: 'Travis Markham', salary: 102000 },
  {name: 'Bertha Jackson', salary: 47000},
  { name: 'Vasya Pupkin', salary: 322000 },
];

const salariesSum = employees
  .reduce((prev, cur) => prev + cur.salary, 0);

console.log(salariesSum); // 701000

The reduce function can be combined with map and filter, but it is important that reduce is the last one in the chain.

Let’s try to calculate the total salary of those employees who earn more than one hundred thousand.

const employees = [
  { name: 'John Richards', salary: 155000 },
  { name: 'Lenny Mingles', salary: 75000 },
  { name: 'Travis Markham', salary: 102000 },
  { name: 'Bertha Jackson', salary: 47000 },
  { name: 'Vasya Pupkin', salary: 322000 },
];

const salariesSum = employees
  .filter(employee => employee.salary > 100000)
  .reduce((prev, cur) => prev + cur.salary, 0);

console.log(salariesSum); // 579000

Enough theory, it’s time to move on to practice!