Javascript
Functional Programming
reduce()

Javascript reduce() method: array index, initial value, object, sum async, break

The Array.prototype.reduce() method in JavaScript is one of the most commonly used array methods, It is a prime example of the functional programming side of JavaScript.

When you first learn to program, you're introduced to procedural and object-oriented programming, The reduce() method is not always easy to understand intuitively. So, in this post, I'm going to take a look at everything from functional programming to various real-world examples to understand the reduce() method.

1. JavaScript Functional Programming in a Nutshell

Functional programming is a programming paradigm that refers to a technique for designing program behavior based on functions in the mathematical sense.

In functional programming, as long as you put the same values into a function, you should always get the same result. A function that follows this principle is called a pure function and should not change any state values in its behavior. If it does, it's called a side effect, and functional programming aims to minimize them.

It also emphasizes the immutability of state and data, so instead of changing the values of a data structure we design our programs to create new data structures. This means that if you want to change the array [1, 2, 3] to [1, 2, 4], you create a new array [1, 2, 4]. Programs designed this way have powerful advantages for things like concurrent programming, where data is accessed simultaneously.

Finally, JavaScript treats functions as first-class citizens - objects that can be used anywhere: variables, parameters, return values, etc, It's a great language for functional programming. As a result, many JS developers use functional programming to write maintainable and reliable code.

Functions that are defined as functional are

const add = (a, b) => a + b;
 
const result = add(1, 2); // Output is always 3

1.1. Why the reduce() method is important for functional programming

The reduce() method adheres to the principles of functional programming in JavaScript because it performs operations on an array without modifying the existing array. This is especially true when you're reducing an array to a single value or accumulating results based on the elements in the array, because you don't have to manage the accumulated values outside of the function. It's also free of side effects. As a result, it's an essential method for writing cleaner, more maintainable code.

2. Understanding the reduce() method: initialization, index, and accumulator

The reduce() method is an array method that traverses an array and executes the provided function (reducer) for each element of the array to produce a single output value.

The basic syntax is

array.reduce(reducerFunction, initialValue);
 
// reducerFunction
array.reduce(((accumulator, currentValue, currentIndex, array) => returnValue), initialValue);

The initialValue is the initial value that will be added to the return value of the reducerFunction. Use 0 to get the sum of numbers, "" to get the final string, etc.

The reducerFunction is a callback function that takes four parameters.

  • accumulator (accumulator) - accumulates the return value of applying the function to all elements up to the previous index.
  • currentValue (currentValue) - The value of the item the function is currently applied to.
  • index (currentIndex) - The array index of the current element (optional).
  • array (array) - a reference to the array itself (optional)

Let's take a look at a simple example to see what values each parameter will have.

const numbers = [ 1, 2, 3 ]
 
numbers.reduce((accumulator, currentValue, currentIndex, array) => {
    console.log(`accumulator: ${accumulator}, currentValue: ${currentValue}, currentIndex: ${currentIndex}, array: ${array} `)
    return accumulator + currentValue
}, 0);
 
// Output:
// accumulator: 0, currentValue: 1, currentIndex: 0, array: 1,2,3
// accumulator: 1, currentValue: 2, currentIndex: 1, array: 1,2,3
// accumulator: 3, currentValue: 3, currentIndex: 2, array: 1,2,3
// 6

At the first call of the reducer function with index 0, the accumulator has an initial value of 0. The reducer function adds the current value 1 to the accumulator value and returns it, which becomes the new accumulator value. This process is repeated until the final result of the reduce() function returns the final accumulator value, 6.

In other words, to unpack the behavior of the above example, it would look like this

const numbers = [ 1, 2, 3 ]
const reducerFunction = element => element
 
const returnValue = reducerFunction(numbers[0]) + reducerFunction(numbers[1]) + reducerFunction(numbers[2])
 
console.log(returnValue)
// Output: 6

2.1. Beware of the break and continue keywords

Most array methods, including the reduce() function, do not support the break, continue keywords that manipulate traversal operations. If you don't want to do anything to the current element, such as using the continue keyword, you can use a conditional statement to return the accumulated value as is. In the example below, we passed through 3 without accumulating.

const numbers = [ 1, 2, 3 ]
const reducerFunction = (accumulator, element) => {
    console.log(`누산기: ${accumulator}, 현재값: ${element}`)
    if (element === 3) {
        return accumulator
    }
    return accumulator + element
}
 
numbers.reduce(reducerFunction, 0)
// Output: 3

3. How to use it to manipulate objects

The reduce() method is also useful for accessing the properties of an object and performing various operations on them. You can access the object properties directly or use a helper method like Object.entries().

Let's look at them one by one.

const people = [
    { name: 'Alice', age: 25 },
    { name: 'Bob', age: 30 },
    { name: 'Charlie', age: 25 },
    { name: 'David', age: 30 }
];
 
const groupedByAge = people.reduce((accumulator, currentValue) => {
    const ageGroup = currentValue.age;
    accumulator[ageGroup] = accumulator[ageGroup] || [];
    accumulator[ageGroup].push(currentValue);
    return accumulator;
}, {});
 
console.log(groupedByAge);
// Output:
// {
//  '25': [
//      { name: 'Alice', age: 25 },
//      { name: 'Charlie', age: 25 }
//  ],
//  '30': [
//      { name: 'Bob', age: 30 },
//      { name: 'David', age: 30 }
//  ]
// }

The example above accessed the age property of an object and performed a task to categorize the objects into two groups based on their age.

const grades = {
    math: 90,
    science: 80,
    history: 85,
    english: 95
};
 
const totalScore = Object.entries(grades).reduce((accumulator, [subject, score]) => {
    return accumulator + score;
}, 0);
 
console.log(totalScore); // Output: 350

If you need to manipulate multiple values of a single object, you can use the Objext.entries() method. This method manipulates the object's properties and property values as a single array, as shown above. This allows us to use the reduce() function on objects as well, and it looks like this, Destructuring assignments, such as [subject, score], can be used to write more concise code.

4. Examples of real-world applications like duplicate removal

In the section JS filter() function - 3. Deduplication with filter(), we also saw the deduplication technique as a code example. With the reduce() function, you can also easily write code to remove duplicate elements from an array.

Let's start with the code.

const array = [1, 2, 3, 2, 4, 3, 5, 1];
 
const uniqueArray = array.reduce((accumulator, element) => {
  if (!accumulator.includes(element)) {
    accumulator.push(element);
  }
  return accumulator;
}, []);
 
console.log(uniqueArray);
// Output: [1, 2, 3, 4, 5]

The above code sets the initial value to an empty array [] and then adds a new element to it if the current element does not exist in the accumulator{js} array. Use the Array.includes() method to determine inclusion.

Compare this to the String.includes() method used in the section JS filter() function - 4. Defining callback functions with includes().

You can see that the result array returned by the reduce function only pulls non-duplicate elements.

5. Using async callback functions

We've seen how to use async callback functions in array methods in JS map() method - 6. Async callback: Asynchronous callback functions. The map() method, along with the Promise.all() method, allowed us to write code that worked asynchronously + concurrently.

However, the reducer method cannot be used with asynchronous callback functions because there is no way to support asynchronous behavior. To get around this, we need to define our own custom reduce function that works asynchronously using the async await keyword and a Promise object.

Let's start with the sample code.

const array = [1, 2, 3, 4, 5];
 
const asyncReduce = async (array, callback, initialValue) => {
  let accumulator = initialValue;
 
  for (const element of array) {
    accumulator = await callback(accumulator, element);
  }
 
  return accumulator;
};
 
const sumAsync = async (a, b) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(a + b);
    }, 1000);
  });
};
 
const calculateSum = async () => {
  const sum = await asyncReduce(array, sumAsync, 0);
  console.log(sum);
};
 
calculateSum();
// Output: 15

The asyncReduce() function in the example traverses the array through the for of syntax, calls the asynchronous callback function, and await for all operations to complete. The arbitrary asynchronous callback function sumAsync() waits one second and then returns the result of the addition. Finally, the calculateSum() function serves as the main function that actually executes the asyncReduce() function.

If you run the code, after a certain amount of time you will get the sum of all numbers, 15.

6. Conclusion

The JavaScript reduce() method is a powerful and versatile tool for functional programming. It allows you to perform operations on arrays while maintaining principles such as immutability and simplicity. Use it wisely in conjunction with other array methods, and make sure that when you write your code, it's intuitive and readable.

copyright for Javascript reduce

© 2023 All rights reserved.