.reduce() for the beginner
Jackie Cisneros
Posted on February 16, 2023
If you are reading this post, you are at the point in your coding journey where you want to start exploring iterator techniques outside of a for loop. You are in a great spot, because .reduce() will take a callBack function (supplied by you, the Software Engineer) and return a single value without necessarily "looping" through your entire array. Here is the code we will be working with:
let arrayOne = [7, 8, 3];
let arraySum = arrayOne.reduce(function(a, b){
return (a + b)
}, 2);
console.log(arraySum) // displays 20
Getting to Know the Code
Take a minute right now to study the code above. Look at the parenthesis, the curly braces, the commas. Try to understand what the language is actually saying, and say it out loud. At the end of this post, we will say out loud together, in human words, what the code is doing. This will greatly improve your skill in technical communication.
In the above example, reduce accepts two parameters: a callBack function (which has two parameters in this example, a and b; it can actually have up to 4, but that is beyond our scope today), and an optional "initial value".
We know that in JavaScript, we have to pass in arguments when we call our functions. So why don't we see arguments here? That is a great question! And that is why reduce is so awesome and helpful. Reduce is a built-in method, which means JavaScript knows what to do when we call it on a particular array, in this case, arrayOne. It uses the array to establish the arguments to pass into the callBack function.
Let's walk line by line through our code and try to think like the program.
Synchronous JavaScript
In the first line, JavaScript sees arrayOne:
let arrayOne = [7, 8, 3];
Because JavaScript is synchronous (meaning it processes our code much like we read books in English: top to bottom, and left to right), we must initialize our array variable before we call our reducer.
Take a quick pause for a minute and try to answer this question: what would happen to the program if we initialized arrayOne after we called our reducer function?
let arraySum = arrayOne.reduce(function(a, b){
return (a + b)
}, 2);
let arrayOne = [7, 8, 3];
The program would throw an error. It would say something like this:
index.js:14 Uncaught ReferenceError: Cannot access 'arrayOne' before initialization
at index.js:14:20
JavaScript is good to us in this way. It can recognize that we want to access an array, but because the program is inherently synchronous, it cannot jump lines of code to go searching for our variable. So, it throws an error saying we aren't communicating with it correctly. How helpful! If you come across an error like this, go ahead and check the composition of your code. Ask yourself, "Did I try to access a variable before I created it? How can I fix that?" In this case, we can simply move the line of code in which we initialize arrayOne above the reduce function.
Storing Our Single Returned Value: Practicing with REPL
In our next line of code, we initialize a variable arraySum:
let arraySum =
This is important because reduce returns a single value from an array. The way to access that returned value is through this variable. Go ahead and open up this REPL for JavaScript. Copy and paste the entire code snippet from the beginning of this post into the left window (make sure you are in node.js in the little drop-down window).
When you hit run, you will see your console.log displayed in the right window as 20. Now in the right window, type arraySum and hit enter. You should see 20. This is because our variable arraySum is set equal to the evaluated reducer function.
Parameters of Reduce: the Anonymous Function and "Initial Value"
Let's return to our code and move to the right of the variable, after the equal sign:
arrayOne.reduce(function(a, b){/*...*/}, 2)
Here we are calling reduce on arrayOne and setting our first parameter, our callBack function. This is an anonymous function (we can tell it is anonymous because it has no name, and it can be anonymous because we do not call it outside of the arraySum variable) with two parameters, a and b. Parameter a is our "accumulator" and parameter b is our "current value". Finally, we have the lonely 2. This is an optional parameter in the reduce method.
HEY WAIT! WHAT HAPPENED TO ALL THE CODE BETWEEN THE CURLY BRACES?
This is a great question. Right now, we are looking at the code the way JavaScript reads it. It doesn't look at the code block between the curly braces; it continues looking down the line at the other parameters of our anonymous function, which is the "initial value". It needs this information before it can execute the function.
What does the "initial value" do? Hold on to your hat, because this is where the ride starts. Let's integrate this question into our discussion about the parameters of our callback function.
Accumulator and Current Value: Parameters of the Anonymous Function
Let's talk about the "accumulator" first, in this case a. This is the value resulting from the previous call to our callBack function. On the first call, it is set to the value of arrayOne[0]. Check out our array below. On the first call of our callBack, where is accumulator set?
let arrayOne = [7, 8, 3];
It is set at arrayOne[0], which is the integer 7.
Now, if we have an "initial value" declared, which we do, our "accumulator" is then set to be the initial value. Check out the code snippet below one more time. What would our accumulator value be set to?
arrayOne.reduce(function(a, b){/*...*/}, 2)
If you guessed 2, you've got it!
Now let's talk about "current value". This is the value of the current element in our array. We start at arrayOne[1]. Look at our array above. Imagine we are still in the first call. What is our "current value"?
arrayOne[1] is the integer 8. Great! Now let's throw in the "initial value" wrench. If this is declared, as in our example, "current value" becomes arrayOne[0], which is 7.
Whew! So to recap, when "initial value" is a player, on the first call of our callBack function, "initial value" takes the place of "accumulator", and the default for "current value" (which is arrayOne[1]) is replaced by the arrayOne[0]. Basically, everything bumps down by one. It is almost like we are adding an element to the beginning of our array and starting there.
Finally, we hit our last line of code:
console.log(arraySum)
JavaScript scans global memory, looking for a variable named arraySum. Aha, it is there, and it evaluates to the call of our reducer function. So now, JavaScript can enter the body of our function.
The Body of the callBack Function and Making Calculations
Before we evaluate the body of our function, let's make a quick cheat sheet for the work JavaScript has done so far and stored in local memory for the first execution context of this function:
arraySum = the evaluated result of calling reduce on arrayOne;
a = 2 (this is our accumulator and first parameter/argument, in this case our "initial value");
b = 7 (this is our current value and second parameter/argument, because we have "initial value" declared, it it arrayOne[0]);
With this information that JS has stored in memory, we can enter the body of our function:
{
return (a + b)
}
JavaScript hits this line of code and knows what a and b represent in this call, as they are stored in the local memory of this function. JavaScript adds them together and returns the math, in this case 9, back to the callBack function. Now we have the value of our "accumulator" for our next call. Remember, we are only in the first call; JavaScripts' job is to do this on ALL of the element specified in the array!
For the final piece of this blog (and the most fun part of .reduce()), we finish up the calculations. In local memory, our parameters are updated, so now:
a = 9 (the evaluated result of the first call, the "accumulator");
b = 8 (the next index in our array, or arrayOne[1]);
JavaScript then enters the body of the function and performs for us:
{
return (a + b)
}
which is 17. Can you guess what we do with the evaluated result of our callBack function?
Yes! We use it as our new accumulator value in the next call.
a = 17;
b = 3;
which is 20. JavaScript sees there are no more elements in the array to iterate over, and exits out of the function entirely, leaving our variable arraySum equal to the returned value of 20, which is stored in global memory.
Here is our entire snippet of code one more time:
let arrayOne = [7, 8, 3];
let arraySum = arrayOne.reduce(function(a, b){
return (a + b)
}, 2);
console.log(arraySum) // displays 20
Technical Communication
Now, as promised, let's look at the code again and talk technically about the code. Try it yourself first. Then read the below paragraph out loud and follow along with the code above:
We are declaring a variable arrayOne and setting it equal to an array of integers including 7, 8, and 3. We are declaring another variable, arraySum, and setting it equal to the evaluated result of calling reduce on arrayOne. Reduce accepts two parameters: the first is an anonymous function that also accepts two parameters, a and b; the second is the initial value of 2. The body of the anonymous function returns adding b to a.
Whew, you did it! Now it it time to play. Go ahead and use the REPL link to try out different parameters. What would happen if you passed in a different initial value, or none at all? What would happen if you had a much larger array and called slice on it before calling reduce on it? What if you had a collection of functions that you called reduce on? Here is a little code snippet to toy with if you're up for the challenge:
const minusThree = x => x - 3
const result = [minusThree].reduce((value, currVal) => currVal(value), 0 )
Happy Coding!
Posted on February 16, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.