My friend Mark Buskbjerg and I are both members of the Vanilla JS Slack channel run by Chris Ferdinandi. Yesterday, Mark asked the channel for help with a challenging array structure. Let’s look at how I helped him come up with a solution using the really versatile
Mark had an array as follows. It’s an array of votes cast by users. The first value in each inner array is the ID of the item the user voted for, and the second value is a string to show whether it was an upvote or a downvote.
Mark wanted to sum up the upvotes and downvotes for each ID in the array. After a discussion, I helped him come up with a solution that does just that.
The first thing I did was create a function to pass into the
Array.reduce() method. The function can accept up to four arguments, but I only needed the first two: the accumulator and the current value. The purpose of the current value is obvious; it’s the current item being processed in the array. As for the accumulator, the MDN Web Docs explain:
The accumulator accumulates the callback’s return values. It is the accumulated value previously returned in the last invocation of the callback.
Here’s the function with its parameters set up.
Let’s pass this into the
Array.reduce() method now, because I need to explain one more thing for it to make sense.
In addition to the callback function, the method also accepts another argument. If supplied, it becomes the first argument to the first call of the callback function. In this case, I used an empty object literal (
On the first invocation of the
sumVotes() callback function, the
accumulator will be equal to the empty object literal, while the
currentValue will be equal to the first value in the array (
Note: if you’re wondering how we can just pass in the name of the function without using any parentheses, I wrote a post about that.
Now, let’s dig into the actual function contents.
Create a property for the ID
For each invocation of the function, we need to create a property for the current vote ID if it doesn’t already exist. To do that, we’ll use a simple
The vote ID is the first item in the current array, so we use
currentValue to reference it. If the
accumulator object doesn’t have the vote ID as a property, we create it and set its value to an array. We set both values in this new array (upvotes and downvotes) to
Upvotes or downvotes?
Now we need to figure out whether we’re increasing the upvotes or the downvotes for this vote ID. We’ll create a variable called
If the second value in the current array is
"up", we’ll set this variable to
0; if it’s
"down", we’ll set it to
1. These numbers correspond to the indices in the array of upvotes and downvotes for the current vote ID. The first item in the array is the upvotes (
0), while the second item is the downvotes (
Increase and return
Finally, we need to increase the upvotes/downvotes and return the accumulated value.
Here’s the complete function:
After running the function through the
Array.reduce() method, the following is the final value that gets returned and saved to the
The ID of each vote is a property of the object. The value of each property is an array. The first value in each array is the sum of upvotes for that ID, while the second value is the sum of downvotes.
Array.reduce() method can be a little confusing. For more on this, I highly recommend you check out the post Using
Array.reduce() in vanilla JS by Chris Ferdinandi. It really helped me get to grips with how it works. In particular, I found the following snippet very helpful:
Most of the modern array methods return a new array. The
Array.reduce()method is a bit more flexible. It can return anything.
Its purpose is to take an array and condense its content into a single value.
That value can be a number, a string, or even an object or new array. That’s the part that’s always tripped me up. I didn’t realize just how flexible it is!