JAVASCRIPT IRL: Examples of JavaScript's reduce function in real life
Tags: javascript, frontend, coding, webdev
Published: 2021-01-10 19:00:23

JavaScript array methods are super useful, and learning how to use them can really help improve the readability of your code. This is the second part of a series on JavaScript array functions, where I dive into examples from real, production codebases. Today's function is `reduce`, which (for me at least) was the most abstract one of the three. By virtue of its abstractness, however, it's also the most powerful. In fact, it's possible to do the jobs of the other two using just `reduce` alone! (Even though you shouldn't. Definitely, absolutely, do not do this.)

Much like the `map` function, `reduce` is called on an array, and takes in two parameters: a callback, and an initial value. However, the callback looks a little different than the one in `map` — instead of taking one parameter, it takes two: an accumulator, and the current element. This gets to the very heart of the `reduce` function: starting with the initial value, it iterates over every element in the array, returning the result of the callback function as the accumulator to the next iteration of the loop. If that sounds confusing, don't worry. That's what the examples are for!

#### A trivial example

Before we get into code, I want to really drill down into what `reduce` does. An analogy that I found really helpful goes as follows:

Imagine a line (i.e. array) of people. You want to find the sum of their ages; that is, you want to _reduce_ your array of people into a single number — their combined age. To do that, you'd probably use a calculator app and go down the line one person at a time, adding to your total as you go. That's exactly what the `reduce` function does — the initial value is 0, the accumulator is the running total in your calculator, and the current element is the person you're currently in front of.

With that in mind, let's see a simple example using the same sort of analogy:
```javascript
      const arrayOfPeople = [
        {
          name: 'John Doe',
          age: 21
        },
        {
          name: 'Mary Sue',
          age: 34
        },
        {
          name: 'Gary Stu',
          age: 43
        }
      ];
      const combinedAge = arrayOfPeople.reduce((acc, curr) => acc + curr.age, 0);

      console.log(combinedAge); // => 98
```
To visualize how this works, use the same line of people analogy. Imagine you have a calculator, and you need to count the combined ages of these three people. You'd start off with 0 in your calculator — that's the initial value. Then you'd go up to John Doe, ask them their age, and add that to the value in your calculator. 0 plus 21 gives 21, so that's the running total so far. Then you'd go up to Mary Sue and ask _them_ for their age. They say 34, so you add that to your calculator; 21 plus 34 gives 55, so now that's your running total. Finally, you'd go up to Gary Stu, ask them their age, and add that in. 55 plus 43 gives 98 — and that's exactly what `reduce` returns.

Now that we have that under our belt, let's look at some real-life examples:

#### Converting HTML nodes to strings

In this example, I was writing a feature for my blog that allowed the user to share a post to [dev.to](https://shaile.sh/devto). I needed to select a bunch of tag elements on my page and convert them into a comma-separated string as part of the post frontmatter. This is the perfect use case for `reduce`; it takes an array of objects and squashes or reduces them down into a single value. Here's how I did it:
```javascript
      const tagString = ` tags:${Array.from(document.querySelectorAll(".tags span.tag")).reduce((acc, curr) => { 
        return acc + (acc == "" ? "" : ", ") + curr.textContent;
      }, "")}` ;
```
Don't be fooled by the complicated looking ternary operator — it's only there to make sure that the first element doesn't have a comma before it. Otherwise, all the `reduce` function is doing is adding commas between the text contents of all of the tags.

Before we move on, a good question is why I couldn't use a function like `join` to do this. The answer is that you can't join an array of HTML nodes — you need to get their `textContent` property to see what they contain. What I could've done instead is `map` each element of the array to their `textContent` and _then_ `join` them, but one method is much better than two. Hence, the `reduce` function. On an unrelated note, if you'd like to see some examples of the `map` function being used, be sure to [check out my article](https://shaile.sh/codes/posts/2021/01/06/javascript-map-in-real-life/).

With that said, let's look at another example:

#### Formatting comments

I recently implemented a comment section on my [blog](https://shaile.sh/codes), and as part of that I wanted users to be able to apply basic formatting to their comments. This included bold, italics, code, and linebreaks. Because I didn't want to use any external libraries or parsers, however, I had to convert raw Markdown data into safe HTML elements. To do this, I needed to separate the input data by line, escape any HTML, and then run a custom Markdown converter on each line.

That seems like a lot, but it's the perfect job for a workhorse like reduce. I can perform the HTML escaping in the callback, and extract any useful code like the Markdown parsing to an external function. Here's what I ended up with:
```javascript
      return body.split('\n').reduce((acc, curr) => {
        let text = document.createTextNode(curr);
        let p = document.createElement('p');
        p.appendChild(text);
        if (curr.trim().length === 0) return acc;
        return acc + (acc === "" ? "" : '</p><p class="comment-body">') + safeMarkdownToHTML(p.innerHTML);
      }, "");
```
The first few lines are just a way to leverage the browser's built-in HTML escaping with the `createTextNode` function. After that, I use a ternary operator (again!) to make sure that the first element doesn't have any unwanted content appended before it. Finally, I return the results of the (recursive) Markdown parsing function. While it may seem like a lot at first, by breaking it down into pieces, we can see how the final product is constructed. In this case, `reduce` serves as one tool among many to achieve this goal. By the way, let me know down in the comments if you'd like to see a post on parsing Markdown — it's a great introduction to recursion and string manipulation.

Let's take a look at one final example:

#### Making JavaScript effects accessible

On several pages on my website — my [home page](https://shaile.sh), [blog page](https://shaile.sh/codes), and [design page](https://shaile.sh/design), for example — I use a typewriter effect as a bit of eye-candy. While cool-looking (I'm quite proud of the effect, to be honest), it's important to recognize that not everybody sees the internet in the same way. This effect in particular is quite inaccessible to people who use screen readers, so I had to find a way to convert the array of disparate words into one long phrase that could be read out via the `aria-label` attribute.

To do this concatenation, I once again reached for `reduce`. Because of the nature of the function, I was able to make a logical, grammatically correct sentence that would make sense when read out. Here's what that `reduce` function looked like in context:
```javascript
      let t = new Typewriter(
        el, 
        el.dataset.speed, 
        el.dataset.pause, 
        JSON.parse(el.dataset.text), 
        [...new Set(JSON.parse(el.dataset.text))]
          .reduce((acc,curr) => acc + ", and " + curr.trim()), "")
      );
```
Super simple, super sweet — all I had to do was add `", and"` between each element of the array. Again, I didn't end up using `join` because I had to call `trim` on each piece of text. Using `reduce` allows the array to be transformed and mutated while it's being collected, which is perfect for this use case. By the way, if you're interested in learning more about accessibility on the web, and in particular with JavaScript, be sure to subscribe to my mailing list — I'm going to have a lot of posts dedicated to the topic in the near to medium future.

#### Wrapping it up

I hope these examples gave you an idea of how the `reduce` function is really used in a codebase, and how it can help make code more readable and versatile. Let me know down in the comments if you have any interesting uses for the `reduce` function, and keep an eye out for the final post in the series!

As always, don't forget to follow me for more content like this. I'm currently writing on [dev.to](https://shaile.sh/devto) and [Medium](https://shaile.sh/medium), and your support on either platform would be very much appreciated. I also have a [membership](https://shaile.sh/support) set up, where you can get early previews of articles and exclusive access to a whole bunch of resources. Also, if you've particularly enjoyed this post, consider supporting me by [buying me a coffee](https://shaile.sh/coffee). Until next time!