ARRAY METHODS | JAVASCRIPT | PROGRAMMING

JavaScript’s Array Operations: Map, Filter and Reduce, Explained

Unlock the full potential of arrays with these powerful methods

Matt Connors
10 min readDec 1, 2024
An image showing how the 3 main methods work together in JavaScript to process and aggregate data, an example of concise and readable functional programming.
Illustration is a screenshot by the author, utilizing the application Notion.

Have you ever wondered how JavaScript developers make complex array operations look so easy?

In today’s article, we’ll discuss what JavaScript’s array methods are and why they are essential for clean, efficient and readable code.

Understanding these methods will make you a better JavaScript developer, helping you with more complex operations, which all of us have to deal with at some point.

These methods also support modern JavaScript paradigms like functional programming.

By the end, you’ll know how to chain these methods to create powerful, readable code.

First, What Is an Array?

Let’s first clarify what arrays are: they are a data structure, denoted with [ and ], which can have any number of elements, preferably of the same type as each other.

The elements in an array are ordered — or indexed — and don’t have to be different to each other like in a mathematical set.

We start counting the index from 0, then 1, then 2 and so on.

Here’s an example of an array in code:

const apples = ["green", "red", "yellow"];
console.log(apples);
// Will output ["green", "red", "yellow"]

In the example above:

"green" is at index 0,
"red" is at index 1, and
"yellow" is at index 2.

Here, “green”, “red” and “yellow” are elements in the apples array, with indexes 0, 1, respectively 2.

Why Are Array Operations Important?

From simplifying the task of categorizing data to easily performing operations on each set of an array, these methods — and a few more — are vital for writing efficient, understandable code.

Understanding Array Operations

What Are Array Operations?

In programming, array methods are functions which are part of another element, either a data structure or a fellow function.

In the syntax array.method(), we specify the array (either the variable that stores the array or simply the entire array), then we add a dot to denote that we are getting something else from the array, and then we write the method’s name, calling it like any other function.

To put it simply, array operations are simply functions which we are calling on a specific array.

How Do We Call Them?

The syntax is array.method().

// Here I am specifying the name of the variable containing the array
const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const evens = digits.filter();
// Here I am writingthe entire array along with the method.
// Most of the time not recommended
const evens = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].filter();

Beware of Immutability

Array operations are not designed to override an already existing value but rather create another value.

let digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let evens = digits.filter();
// Here, .filter() won't be overriding and storing the new values inside digits, but will store them inside evens.
let digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
digits = digits.filter();
// Here, .filter() will override the values already inside digits and put the new values there.

While it is possible to override an already existing value, it is not recommended in most cases.

Map, Filter and Reduce

These are the big 3 operations, which act as incredibly versatile tools.

.map(), .filter()and .reduce() transform, filter, and, respectively, aggregate data.

The Key Methods

Map(): Transforming Elements

The .map() method transforms each element in an array and returns a new array.

Here’s an example of the syntax:

const numbers = [1, 2, 3];
const doubled = numbers.map(n => n * 2);
console.log(doubled); // [2, 4, 6]

Ok, what is happening there? Well, it’s quite simple.

.map() takes a function as an argument, runs that function for each element in the array, then returns a new array with all the new values.

It can be either a normal or arrow function, but it is common practice to use arrow functions for brevity.

Syntax of Map

numbers.map(n => n * 2);
// numbers is the name of the array
// .map(...) is the method we are calling
// n => n * 2 is the function that we are using as a parameter for map
// n (before =>) is a placeholder for the array element being worked on.
// n * 2 is the operation we want performed on each element.
// If the operation was more complex, we would have had to use curly braces {} to encapsulate the entire function

If we were to add another parameter after the first, that one would give us access to the index of the current element.

numbers.map((n, i) => n * i);
// Multiplies each element by its index
// input array: [1, 2, 3]
// output array: [0, 2, 6]

Practical Use of Map

I have been working on a personal project for a while, essentially a CNP encoder/decoder (ID number for all Romanians, it’s on Wikipedia but it is in Romanian).

I had to use .map() to generate a checksum digit, here is the relevant code:

const constant = [2, 7, 9, 1, 4, 6, 3, 5, 8, 2, 7, 9];
const exampleCNP = [3, 7, 3, 1, 1, 2, 5, 5, 2, 9, 1, 1];
const stepOneForChecksum = exampleCNP.map((x, i) => x * constant[i]);
// x is the current element
// i is the index of the current element (starts from index 0)
// The function is multiplying the current element by its corresponding element in the constant

Filter(): Sorting Elements

The .filter() method returns a new array, only containing values which passed a specific condition.

The first parameter is the current element in the iteration, while the second, optional parameter, is the index of the current element.

const numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const evens = numbers.filter(n => n % 2 === 0);
console.log(evens); // [0, 2, 4, 6, 8]
// % (modulo operator) returns the remainder of the division

Syntax of Filter

const evens = numbers.filter(n => n % 2 === 0);
// evens is the variable that will store the new array
// numbers is the array that we are filtering items from
// .filter(...) is the method being called onto numbers
// n => is the parameter for the .filter() method, it is the current element
// n % 2 === 0 is the condition, if it evaluates to true for the current element, that element will be included in the new array

Use Cases?

.filter() is often used to get specific data from API calls or to remove unwanted elements from an array.

Reduce(): Data Aggregation

The .reduce() method aggregates array elements into a single value.

const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const sum = digits.reduce((acc, curr) => acc + curr, 0);
// acc is the value that we start with, in this case it is the sum that we will add to with each new element
// curr is the current value, what we add up to acc
// , 0 is the initial value, saying that when the function is called, acc is to have a value of 0 and work from there

Syntax of Reduce

const sum = digits.reduce((acc, curr) => acc + curr, 0);
// sum is the number that will hold the returned value
// digits is the array that is being reduced
// .reduce(...) is the method being called onto digits
// (acc, curr) are the parameters being given to the inline function, one for the current total, and one for the current element
// After those two, you can also add one more parameter to represent the current element's index.
// => acc + curr is the operation that is to be repeated until the entire array is turned into one singular number
// The 0 at the end denotes the initial value of acc

Use Cases

Reduce can be used in a lot of different ways.

You can use it to calculate totals, as seen above, as well as subtractions by using a minus instead of a plus inside the repeated operation.

You can use it, for example, to calculate the average from an array by combining it with a division:

const numbers = [9, 7, 2, 6, 8, 1, 3, 5, 4];
const averageOfNumbers = (numbers.reduce((acc, curr) => acc + curr, 0)) / numbers.length;
console.log(averageOfNumbers); // 5
// Here, we sum up the array
// Then, encapsulating the expression into parentheses
// We divide the result by the length of the array, giving us the average ****

Other Array Operations

ForEach(): Mapping, But for Mutation

The .forEach() method executes a function for each array element without returning a new array, making it useful for logging or modifying external variables.

const numbers = [1, 2, 3];
numbers.forEach(n => console.log(n)); // Logs: 1, 2, 3
// numbers remains untouched, but the forEach method takes each value and runs it through the console.log() function

Find() and FindIndex(): Locating Elements and Indices

These methods take a function as a parameter just like .filter() except that they only return the first element that passes the test or, in the case of .findIndex(), the index of the first element that passes the test.

const numbers = [1, 2, 3, 4];
const secondEven = numbers.find(n => n % 4 === 0);
console.log(secondEven); // 4

Chaining Array Methods For Some Additional Complexity

const products = [
{ name: 'Apple', price: 1, category: 'fruit' },
{ name: 'Carrot', price: 0.5, category: 'vegetable' },
{ name: 'Banana', price: 0.8, category: 'fruit' },
]; // Declare a variable 'products' which contains an array of objects
const totalFruitCost = products // Define 'totalFruitCost'
.filter(product => product.category === 'fruit') // Select only the fruits
.map(product => product.price) // Access each product's price
.reduce((acc, price) => acc + price, 0); // Sum the cost of all the fruit
console.log(totalFruitCost); // 1.8

Notice how I didn’t put a semicolon in between the different methods? Yeah, you can do that!

It’s called chaining, and it is incredibly useful sometimes — this is just an example.

Pitfalls and Best Practices

Mutating vs Non-Mutating Methods

Mutating methods cause something we in programming call ‘side effects’.

That is, you sometimes can’t be sure that what you are changing won’t impact something else in the code, so it is almost always better to simply not mutate values into other values, as it is not only possible, but sometimes highly likely, given how error-prone the human mind is, to make mistakes.

Most of the time, mistakes will just cause an error, and that is still bad enough, but you can at least fix it.

Other times, however, you will have a ‘quiet’ mistake. A mistake that just continues to work, and you might not catch it until something more critical breaks.

Point of the story, it is almost always better to use non-mutating methods unless you have a reason not to.

And as I mentioned before, a non-mutating method can quickly become a mutating method if not implemented correctly:

let array1 = [2, 4, 6];
let array2 = array1.map(n => n * n);
// Here, map doesn't mutate the original array

let array1 = [2, 4, 6];
array1 = array1.map(n => n * n);
// Here, map does mutate the original array

So, you need to be careful about this when writing code.

Performance Considerations

Sometimes, chaining methods together might be more performance intensive, especially if you’re dealing with a particularly large array or if you are chaining too many methods together.

const products = [
{ name: 'Apple', price: 1, category: 'fruit' },
{ name: 'Carrot', price: 0.5, category: 'vegetable' },
{ name: 'Banana', price: 0.8, category: 'fruit' },
];
const totalFruitCost = products
.filter(product => product.category === 'fruit')
.map(product => product.price)
.reduce((acc, price) => acc + price, 0);
console.log(totalFruitCost); // 1.8
// This might become performance intensive with more elements in the array or if you're checking for more conditions

For large arrays or performance-critical applications, go through your code and consider alternatives like for loops when necessary.

Readable Code

Always make sure you write for readability first, optimization always comes second, especially if you’re working in a team, because one person’s misinterpretation of confusing code might doom a project.

Common Use Cases in Real-World Applications

You can use array operations to do a lot of different things, for example:

  • Processing a returned JSON string from an API call,
  • Aggregation and visualization of data, and
  • List rendering in front-end frameworks like React.

And also, as I showed, there are a lot of different, more niche use cases, like in my example of having to map the result of a multiplication on two corresponding elements of two separate arrays.

Practice Problems

Before I conclude this article, I would like to give you a little challenge, see if you can complete these!

  1. Transform an array of strings to uppercase using .map().
  2. .filter() an array to include only numbers greater than 10.
  3. Calculate the product of an array of numbers using .reduce().

Hint: Remember the toUpperCase() method for strings!

Conclusion

From averaging out your test scores to mapping out a certain legally defined algorithm, I hope I have convinced you of the power that these methods possess, and that I have been able to teach you enough about them so that you can start implementing them in your own projects.

Thank you for reading! If you enjoyed this article, consider commenting your common uses of array operations, or perhaps suggest something I should cover next, I would love to hear it!

Now that you understand these methods, take the practice problems as your next step toward mastering functional programming in JavaScript!

Acknowledgements

All illustrations in this article were created by the author using Notion.

Some AI assistance was used to streamline the structure and clarify technical language.

--

--

Matt Connors
Matt Connors

Written by Matt Connors

0 Followers

Tech enthusiast sharing science, modern history and occasionally social issues

Responses (1)