The Power of Reduce: Optimizing JavaScript Code for Speed and Efficiency
Tags: javascript, webdev, frontend
Published: 2024-05-14 08:37:30

Hey everybody! This is my first-ever post!

In this article, I will be talking about the **reduce** method of _JavaScript arrays_, which I feel is sometimes forgotten. I will give a brief introduction to this method, and then I will proceed to compare it with other iteration methods. 

##TLDR
 - Review your array iterations
 - Instead of `map` and `filter`, use `reduce`;
 - _Spreading_ (as in `[...list]`) large lists is a bad idea; _Push_ to `list instead` 
 - `reduce` is faster than `map` and `filter`, and `for in` for large lists

## Personal Notes
I'm a _Frontend Developer_ with less than 2 years of experience and this is my <u>first article</u>.

I'm saying this to let you know that I'm still very new, so all feedback is welcome, whether it's about technical content, writing, or personality (please keep it constructive).

## Motivation: the path to a single loop
While reviewing some people's code (and sometimes my own 😬), I realized that it's really easy to employ <u>unnecessary iterations of arrays</u> and that sometimes `reduce` could improve the code.

For example, if I have a list of people and I want to get the list of names of all adults, it is easy to think: "First I need to `filter` the list and get all the adults (age > 18 years in my country) and then I need to `map` it and return their names".
I believe that _arrow functions_ makes this type of thought even easier because it eliminates the need to write `function`. 

However, what keeps you from iterating the list of people and adding a person's name to an empty list if they are an adult. <u>This is `reduce` in a nutshell</u>.
This can also be done with ease using a `for` loop, although occasionally people overlook this method as well.

---

## _Reduce_ method introduction
I will now give a short introduction to how the `reduce` method works, so if you are already familiar with it, be sure to jump to the benchmark section.

### Theory
In JavaScript, we have <u>more than one way to iterate an _array_</u>, like `map`, `for`, or `reduce`. The last one is perhaps the less 'friendly' and common between the three, but instrumental as well, as we shall see. 

The method receives two arguments:
- a _callback function_, that will be used to return the accumulated value in each iteration, and
- an initial value, which will be used to start the accumulation.

The callback function itself receives up to four arguments:
- The `accumulator`, sometimes referred to as `total` or abbreviated to `acc`, is the value that is "added" onto each iteration;
- The `currentValue`, i.e, the current element of the _array_ being iterated
- Optionally, the `currentIndex`, the position of the `currentValue` in the _array_. Commonly used;
- Optionally, the _array_ being iterated. It's rarely used since you typically are iterating a variable that you declared previously.
It is useful nonetheless because if you chain methods (e.g. you call `initialArray.sort(...).reduce(...)`), then the _array_ parameter will be the sorted array and not the initial one.

You might notice that I'm employing terms related to *accumulate*.  This is because that is what reduce does: it starts with one value, and each iteration it will "add" onto that value (I added quotation marks because algebraic addition is not necessarily involved). 


Let's look at some code:

### Practical Examples

I'm just going to give you some basic examples of how to use reduce because my goal isn't to turn you into `reduce` expert (nor do I claim to be) but to keep this method in the back of your mind.

**Retrieving the names of all Adults**
Let's focus on the previous example. If you were to do the same with `map` and `filter`, you would do something like:

```javascript
peopleList.filter((person) => person.age >= 18)
          .map((adult) => adult.name);
```

With `reduce`, you'll do something like this:

```javascript
peopleList.reduce((acc, person) => {
  if (person.age >= 18) acc.push(person.name);
  return acc;
}, []);
```

This way, you only use one loop instead of two. Is it less 'friendly'? You tell me.

There are a lot more uses of `reduce`, and ways of using it. I opted to use an `arrow function` but you don't have to if you don't want. In this [CodePen](https://codepen.io/Diogo-Almeida-the-vuer/pen/dyLdJZY) you can find:
- A comparison of the retrieval of the adults' names between `map` and `filter`, `for in` and `reduce`;
- Another example, is where you sum all the numbers in a list. This is a typical example introduced when using `reduce`.

    The reason I'm not detailing it here is that I don't want the concept "`reduce` is for sums only" to settle in your head. Yes, it is used for that, but not exclusively, as a teacher once told me.

    In this example, I also detail how you can use `reduce` with regular functions and inline returns;


---

## Benchmarking iteration methods
Now, those who know me know that I am a bit obsessed with avoiding repetition and increasing speed and performance. 

This is the origin of my article. 

I wanted to test if there was any difference performance-wise between `map` and `filter`, and `reduce`. Then I decided to throw `for in` in the mix as well.

### Always learning!

So I did an initial benchmark and I quickly found a problem:

**⚠ _Spreading_ slows the `reduce` method!**

See, in the _reducer function_, I was returning the `acc` the following way:

```javascript
return condition ? [...acc, item] : acc;
```

With short lists, this isn't much of a problem, but with colossal-sized lists, this syntax made the `reduce` method much slower (as you will see).

Imagine my surprise when I first developed the benchmark and saw that `reduce` (the method I am advocating in my article) was much slower than `map and filter`! However, after some research, I found out that this was because in each iteration an object is created, and thousands of elements are _spreaded_ onto it, so a strategy change was needed:

```javascript
if(condition) acc.push(item);
return acc;
```

Now, whenever I employ `reduce` in my projects, instead of _spreading_, I _push_ the item to the `acc`.


### The benchmark
So, to develop my benchmark I created another [CodePen](https://codepen.io/Diogo-Almeida-the-vuer/pen/LYvOjGN) where:
- A list of 100,000 random numbers is created;
- Each method runs a certain task 100 times, and each execution is timed;
- And an average is obtained for each method;
Regardless of the iteration and method, the task itself is to obtain the binary converted numbers smaller than 16. It's not a "real world" example, but it'll do for this purpose.

You'll see that I added the `reduce` method with _spread_ and _push_ so that you can see the difference in performance. So let's see what the fastest method is.

### Results: And the winner is...
**Reduce**! Of course, it's `reduce`. I wouldn't be making this article if it wasn't. 😄

![Iteration methods time comparison](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cnhafmgnak84mgoil2g5.png)


<!-- Image in Dark Mode ftw -->

You'll notice I didn't add the `reduce` with _spread_ time to the graphic because it was <u>8.8 **seconds** long</u> (over 3000 times slower than with push)! All other methods are measured in **milliseconds**.

As you can see, the difference between the three iteration methods is not very large, but it doesn't change the fact that `map` and `filter` are 3 milliseconds slower than `reduce`. Of course, with smaller lists, the difference will be minimal. 

<u>Some things to consider</u> about this benchmark are:
- The code may seem overcomplicated, but that was the best way I found to avoid re-creating functions with each iteration or method.
- I pushed the list's length to reaches that probably aren't reasonable. It's rare the project where one deals with lists with thousands of elements. But if you are, and performance is a big issue in your website, _reduce_ is your friend;
- Dealing with big lists isn't usually handled in pure JS. There are several ways to handle them, such as _virtualization_ or _pagination_, so be warned.
- The times I presented were obtained by running the code on _CodePen_, so in production, on a server, the execution can be faster.

---

That's all folks! 
Thank you for reading my first post, and I hope you found it useful in some way (I don't refund the time you spent on it otherwise)


## References and links
- [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)
- [W3 Schools](https://www.w3schools.com/jsref/jsref_reduce.asp)
- [Reduce Introduction CodePen](https://codepen.io/Diogo-Almeida-the-vuer/pen/dyLdJZY?editors=0012)
- [Reduce Benchmark CodePen](https://codepen.io/Diogo-Almeida-the-vuer/pen/LYvOjGN?editors=0010)