All the JavaScript (ES6+) Array methods you need

George Roubie
11 min readJul 16, 2021

--

I will explain to you with examples the methods: filter, map, reduce, find, findIndex, includes, some, every, flat and flatMap. Get ready because this article is huge, but it will be a reference for you in the future.

filter

One of the most common actions in an array is to filter it. With the filter, we can easily do it.

Example

Save all female users to a separate array.

Without filter:

const users = [
{ name: 'George', gender: 'male' },
{ name: 'Georgia', gender: 'female' }
];
const femaleUsers = [];
for (let i = 0; i < users.length; i++) {
if (users[i].gender === 'female') {
femaleUsers.push(users[i]);
}
}

With filter:

The filter gets as the first parameter a predicate function.

A predicate function is a function that returns a boolean.

This callback function has 3 parameters:

  1. The current element of the array (required)
  2. The index of the current element in the array
  3. The entire array

The filter creates a new array with all elements that satisfy the provided function.

// Version 1const users = [
{ name: 'George', gender: 'male' },
{ name: 'Georgia', gender: 'female' }
];
const findFemales = function(currentUser) {
return currentUser.gender === 'female';
};
const femaleUsers = users.filter(findFemales);

With destructuring, we can do it like this. If you don’t know what destructuring is, don’t worry read this.

// Version 2const users = [
{ name: 'George', gender: 'male' },
{ name: 'Georgia', gender: 'female' }
];
const findFemales = function({ gender }) {
return gender === 'female';
};
const femaleUsers = users.filter(findFemales);

We can use the arrow function to simplify this.

// Version 3const users = [
{ name: 'George', gender: 'male' },
{ name: 'Georgia', gender: 'female' }
];
const findFemales = ({ gender }) => gender === 'female';
const femaleUsers = users.filter(findFemales);

Now we can remove the function declaration and pass it directly into the filter.

// Version 4const users = [
{ name: 'George', gender: 'male' },
{ name: 'Georgia', gender: 'female' }
];
const femaleUsers = users.filter(
({ gender }) => gender === 'female'
);

I prefer version 3 so in this article, I will use the version 3 style.

map

Many times we need to manipulate the data of the array. With the map, we can easily do it.

Example

Change the response data so that the male value of the gender to be M and the female value of the gender to be F. Also, change the property name to fullname.

Without map:

const response = [
{ name: 'George', gender: 'male' },
{ name: 'Georgia', gender: 'female' }
];
const users = [];
for (let i = 0; i < response.length; i++) {
users.push({
fullname: response[i].name,
gender: response[i].gender === 'male' ? 'M' : 'F'
});
}

With map:

The map gets as the first parameter a callback function that returns a new item for the array. This callback function has 3 parameters:

  1. The current element of the array (required)
  2. The index of the current element in the array
  3. The entire array

The map creates a new array populated with the results of the provided function.

const response = [
{ name: 'George', gender: 'male' },
{ name: 'Georgia', gender: 'female' }
];
const transformResponse = ({ name, gender }) => {
return {
fullname: name,
gender: gender === 'male' ? 'M' : 'F'
}
};
const users = response.map(transformResponse);

reduce

When we need to create a new value based on an array, reduce is very helpful.

Example 1

Save the summary of the array items in a variable.

Without reduce:

const numbers = [1,2,3,4,5,6,7,8,9];
let summary = 0;
for (let i = 0; i < numbers.length; i++) {
summary += numbers[i];
}

With reduce:

The reduce gets as the first parameter a callback function that returns a new item for the array. This callback function has 4 parameters:

  1. The accumulated value previously returned (required)
  2. The current element of the array (required)
  3. The index of the current element in the array
  4. The entire array

The second parameter of the reduce is the initial value. If there is no initial value the first element in the array will be used as the initial value for the accumulator and the loop will begin from the second item on the array.

const numbers = [1,2,3,4,5,6,7,8,9];
const addNumber = (acc, number) => {
return acc + number;
}
const summary = numbers.reduce(addNumber);

We didn’t pass an initial value because we wanted to use the first item of the array as the initial value.

Example 2

Save the number of users that are female to a variable.

Without reduce:

const users = [
{ name: 'George', gender: 'male' },
{ name: 'Georgia', gender: 'female' }
];
let femaleUsersCount = 0;
for (let i = 0; i < users.length; i++) {
if (users[i].gender === 'female') {
femaleUsersCount++;
}
}

With reduce:

const users = [
{ name: 'George', gender: 'male' },
{ name: 'Georgia', gender: 'female' }
];
const reduceFemaleCount = (acc, user) => {
if (user.gender === 'female') {
return acc + 1;
}
return acc;
};
const femaleUsersCount = users.reduce(reduceFemaleCount, 0);

You must always return a value in the callback function, otherwise, you will lose the value of the accumulator. Rember that in every loop we return the new accumulated value.

Let’s make it a little better with destructuring.

const users = [
{ name: 'George', gender: 'male' },
{ name: 'Georgia', gender: 'female' }
];
const reduceFemaleCount = (acc, { gender }) => {
if (gender === 'female') {
return acc + 1;
}
return acc;
};
const femaleUsersCount = users.reduce(reduceFemaleCount, 0);

And one final optimization with the use of the ternary operator.

const users = [
{ name: 'George', gender: 'male' },
{ name: 'Georgia', gender: 'female' }
];
const reduceFemaleCount = (acc, { gender }) => {
return gender === 'female' ? acc + 1 : acc;
};
const femaleUsersCount = users.reduce(reduceFemaleCount, 0);

Example 3

Change the response data so that the male value of the gender to be M. Also, change the property name to fullname. And save only the male users to a new array called maleUsers.

Without reduce:

const response = [
{ name: 'George', gender: 'male' },
{ name: 'Georgia', gender: 'female' }
];
const transformResponse = ({ name, gender }) => {
return {
fullname: name,
gender: gender === 'male' ? 'M' : 'F'
}
};
const findMales = function({ gender }) {
return gender === 'M';
};
const users = response.map(transformResponse);
const maleUsers = users.filter(findMales);

As you see we first changed the structure and the data with the map method and then we used the filter method to get the male users. In these cases, the reduce is a necessity.

With reduce:

const response = [
{ name: 'George', gender: 'male' },
{ name: 'Georgia', gender: 'female' }
];
const reduceMaleUsers = (acc, { name, gender }) => {
if (gender === 'male') {
return [...acc, { fullname: name, gender: 'M' }];
}
return acc;
};
const maleUsers = response.reduce(reduceMaleUsers, []);

As you know with the reduce we create a new single value but this value can be anything. Here we are creating a new array with only the male users and the data in the correct format. Filter and map together, super cool!

As the initial value, we used an empty array in order to fill it. If you don’t understand the ...acc , don’t worry read this.

find

One of the most common actions in an array is to find an element inside an array. With the find, we can easily do it.

Example

Find the user with id: 123, and save it to a variable.

Without find:

const users = [
{ id: 123, name: 'George' },
{ id: 456, name: 'Georgia' }
];
let user;
for (let i = 0; i < users.length; i++) {
if (users[i].id === 123) {
user = users[i];
break;
}
}

With find:

The find gets as the first parameter a predicate function. This callback function has 3 parameters:

  1. The current element of the array (required)
  2. The index of the current element in the array
  3. The entire array

The find returns the value of the first element in the array that satisfies the provided testing function.

const users = [
{ id: 123, name: 'George' },
{ id: 456, name: 'Georgia' }
];
const findUser = ({ id }) => id === 123;
const user = users.find(findUser);

Keep in mind that if no element of the array does not satisfy the provided testing function the find will return undefined.

findIndex

The method find will return undefined if it will not find the element. This is a problem in the case that it finds the element and the element has the value of undefined . In this case, you don’t know what actually happened.

So if you want to be sure if an element exists in an array and you don’t want its value use the findIndex.

Example

Find the index of the user with id: 123, and save it to a variable.

Without findIndex:

const users = [
{ id: 123, name: 'George' },
{ id: 456, name: 'Georgia' }
];
let userIndex = -1;
for (let i = 0; i < users.length; i++) {
if (users[i].id === 123) {
userIndex = i;
break;
}
}

With findIndex:

The findIndex gets as the first parameter a predicate function. This callback function has 3 parameters:

  1. The current element of the array (required)
  2. The index of the current element in the array
  3. The entire array

The findIndex returns the index of the first element in the array that satisfies the provided testing function. Otherwise, it returns -1.

const users = [
{ id: 123, name: 'George' },
{ id: 456, name: 'Georgia' }
];
const findUserIndex = ({ id }) => id === 123;
const userIndex = users.findIndex(findUserIndex);

includes

When the array items are primitive types e.g. strings, numbers e.t.c. there is an easier way to find out if an element exists in an array with the includes.

Example 1

Find if the number 9 is in the array.

Without includes:

const numbers = [1,2,3,4,5,6,7,8,9];
const indexOf9 = numbers.indexOf(9);
if (indexOf9 !== -1) {
console.log('9 exist');
} else {
console.log('9 does not exist');
}

The indexOf is the old way to find out if an element exists based on its position in the array. I actually never liked this approach because you have to check the -1 position, and it's not self-explained.

With includes:

The includes gets as the first parameter the value to search for and returns true if it finds it, otherwise, it returns false.

const numbers = [1,2,3,4,5,6,7,8,9];
if (numbers.includes(9)) {
console.log('9 exist');
} else {
console.log('9 does not exist');
}

Now the code with the includes is self-explained and simpler to understand, even if you don’t know what includes do.

Example 2

Find if a NaN is in the array.

Without includes:

const numbers = [1,2,3,4,5,6,NaN,7,8,9];
const indexOfNaN = numbers.indexOf(NaN);
if (indexOfNaN !== -1) {
console.log('NaN exist');
} else {
console.log('NaN does not exist');
}

Actually, this code does not work so the use of includes is required for the NaN.

With includes:

const numbers = [1,2,3,4,5,6,NaN,7,8,9];
if (numbers.includes(NaN)) {
console.log('NaN exist');
} else {
console.log('NaN does not exist');
}

some

Many times we need to check if some elements in an array match a statement. With the some, we can easily do it.

Example

Save to a boolean variable true if there is at least one male user.

Without some:

const users = [
{ name: 'George', gender: 'male' },
{ name: 'Georgia', gender: 'female' }
];
let hasMaleUsers = false;
for (let i = 0; i < users.length; i++) {
if (users[i].gender === 'male') {
hasMaleUsers = true;
break;
}
}

With some:

The some gets as the first parameter a predicate function. This callback function has 3 parameters:

  1. The current element of the array (required)
  2. The index of the current element in the array
  3. The entire array

The some returns true if at least one element in the array satisfies the provided testing function, otherwise, it returns false.

const users = [
{ name: 'George', gender: 'male' },
{ name: 'Georgia', gender: 'female' }
];
const isMale = ({ gender }) => gender === 'male';
const hasMaleUsers = users.some(isMale);

every

Many times we need to check if every element in an array matches a statement. With the every, we can easily do it.

Example

Save to a boolean variable true if all the users are females.

Without every:

const users = [
{ name: 'George', gender: 'male' },
{ name: 'Georgia', gender: 'female' }
];
let allFemales = true;
for (let i = 0; i < users.length; i++) {
if (users[i].gender !== 'female') {
allFemales = false;
break;
}
}

With every:

The every gets as the first parameter a predicate function. This callback function has 3 parameters:

  1. The current element of the array (required)
  2. The index of the current element in the array
  3. The entire array

The every returns true if every element in an array satisfy the provided testing function, otherwise, it returns false.

const users = [
{ name: 'George', gender: 'male' },
{ name: 'Georgia', gender: 'female' }
];
const isFemale = ({ gender }) => gender === 'female';
const allFemales = users.every(isFemale);

Note: Calling every on an empty array will return true for any condition!

flat

If you have an array that the elements of the array are arrays and you want to flatten this, you can do it with the flat method.

The flat gets only one parameter, the depth level. The depth level specifying how deep a nested array structure should be flattened. The parameter is not required because it has a default value of 1.

Usage

const numbers = [1,2,3,[4,5],[[6,7]]];
console.log(numbers.flat()); // [1,2,3,4,5,[6,7]]
console.log(numbers.flat(1)); // [1,2,3,4,5,[6,7]]
console.log(numbers.flat(2)); // [1,2,3,4,5,6,7]

Tip: The bigger the depth level the slower will be the process.

flatMap

The flatMap is the same as the map but it also does a flatting in the array of depth 1. You can’t change the depth level in the flatMap.

Example 1

Flatten the array and change the value of each element to its square value.

Without flatMap:

const numbers = [1,2,3,[4,5],[6,7]];
const flattedNumbers = numbers.flat();
const squareNumber = (n) => n * n;
const squaredNumbers = flattedNumbers.map(squareNumber);

With flatMap:

The flatMap gets as the first parameter a callback function that returns a new item for the array. If the item is an array it will be flattened.

This callback function has 3 parameters:

  1. The current element of the array (required)
  2. The index of the current element in the array
  3. The entire array

The flatMap creates a new array populated with the results of the provided function.

const numbers = [1,2,3,[4,5],[6,7]];
const squareNumber = (item) => {
if (Array.isArray(item)) {
return item.map((i) => i * i);
}
return item * item;
};
const squaredNumbers = numbers.flatMap(squareNumber);

We have to check if the item is an array because the 4th and the 5th item of the array are arrays and if you do array * array it will result NaN. That’s why when it’s an array, we change the value of its elements.

Example 2

Flatten the array and change the value of each element to its square value. Calculate the square of numbers smaller than 6.

Without flatMap:

const numbers = [1,2,3,[4,5],[6,7]];
const smallerThanSix = (n) => n < 6;
const squareNumber = (n) => n * n;
const squaredNumbers = numbers
.flat()
.filter(smallerThanSix)
.map(squareNumber);

With flatMap:

const numbers = [1,2,3,[4,5],[6,7]];
const transformNumbers = (item) => {
if (Array.isArray(item)) {
return item.reduce((acc, i) => {
if (i < 6) {
return [...acc, i * i];
}
return acc;
}, []);
} else if (item < 6) {
return item * item;
}
return [];
}
const squaredNumbers = numbers.flatMap(transformNumbers);

The trick here is that if you return an empty array in the flatMap callback function you are not adding anything in the array. So you are doing the filtering of the array. Also here I have used reduce to do mapping and filtering in the array.

I am sure that if you understand all these ES6+ Array methods you will level up your code and maybe delete some third-party library.

--

--

George Roubie
George Roubie

Written by George Roubie

Experienced Software Engineer with more than 10 years of experience, specialized in Front-End Web technologies, with strong foundations in programming.

No responses yet