JavaScript Reduce

JavaScript Reduce

What can your reduce do....?

As it's already known, reduce is one of the most powerful array methods if not the most powerful. with reduce, you can do virtually all of the other javascript array methods.

The major advantage reduce has over other array methods is the fact that the return type is not limited to an array.

JavaScript Reduce Explained

The javascript Reduce has the following parameters:

Array.prototype.reduce(callbackFn(acc, cur, index, array) => (), initialValue)
  • Callback function:

    • Accumulator: The accumulator is the single value (Note: value here can be of any data type, string, number, array, or object) we get at the end of the execution after the reducer’s job is completed. the accumulator should always be returned at the end of the function.

    • Current value: This is the value of the current element executed by the callback function.

    • Index: The current index in the iteration.

    • Array: The array we are performing the reduce on.

  • Initial Value:

    The initial value is the value the accumulator is initialized with the first time the callback function is called, if specified the accumulator will be initialized with the specified value, if not specified the first item in the array will be used as the initial value. It is a good practice to always specify the initial value and it is also important to understand that the return type is highly dependent on the initial value, i.e if the initial value is a number then the return of the reduce function would be a number if the initial value is an object the return type would be an object

Below are some areas where reduce shines

// calling reduce without initial value
const num : number[] = [1,2,3,4,5]
const sum = num.reduce((acc,cur, _index, _arr)=> {
    acc += cur
    return acc
})
console.log("sum :>>", sum) // sum :>> 15

// what happens
// firstcall acc => 1 current => 2 returnvalue => 3
// second call acc => 3  current => 3 returnvalue => 6
// third call acc => 6  current => 4 returnvalue => 10
// fourth call acc => 10 current => 5 returnvalue => 15

// calling reduce with initial value
const num : number[] = [1,2,3,4,5]
const sum = num.reduce((acc,cur, _index, _arr)=> {
    acc += cur
    return acc
},10)
console.log("sum :>>", sum) // sum :>> 25

// how it worked
//firstcall acc => 10 current => 1 returnvalue => 11
//second call acc => 11  current => 2 returnvalue => 13
//third call acc => 13  current => 3 returnvalue => 16
//fourth call acc => 16 current => 4 returnvalue => 20
//fifth call acc => 20 current => 5 returnvalue => 25

Using Reduce to Flatten Array

const arr = [
   [1, 3],
   [4, 5, 5],
   [5, 2, 6, 4],
   [8]
];
const flattenArray = arr.reduce((acc, cur) => {
     acc = acc.concat(cur)
     return acc
},[]);

comsole.log("flattenArray :>>", flattenArray)
// flattenArray :>> (10) [1, 3, 4, 5, 5, 5, 2, 6, 4, 8]

// how it worked
//firstcall acc => [] current => [1, 3] returnvalue =>[1, 3]

//second call acc => [1, 3]  current => [4, 5, 5] returnvalue => [1, 3, 4, 5, 5]

//third call acc => [1, 3, 4, 5, 5]  current => [5, 2, 6, 4] returnvalue => [1, 3, 4, 5, 5,5, 2, 6, 4]

//fourth call acc => [1, 3, 4, 5, 5,5, 2, 6, 4] current => [8] returnvalue => [1, 3, 4, 5, 5,5, 2, 6, 4,8]

Using Reduce to remove duplicate

const myArray = ["a", "b", "a", "c"];
const unique = myArray.reduce((acc, cur) => {
  if (!acc.includes(cur)) {
    return [...acc, cur];
  }
  return acc;
}, []);

console.log("unique :>>", unique); // ['a','b','c']

//how it worked
//firstcall acc => [] , cur => "a" the condition check if a is in        acc if not a is put in acc => ['a']
// secondcall acc => ['a'], cur => 'b' same as above return => ['a','b']
// thirdcall acc => ['a','b'] cur => 'b' now because 'b' is in acc we skip adding the second 'b' based on the condition. return ['a','b']
// forthcall acc => ['a','b'] cur => 'c', we fall into the check again and 'c' is not in acc so return value => ['a','b','c']

Using Reduce to count occurrence

const myArray : string[] = [
  "mango",
  "apple",
  "mango", 
  "hash",
  "node",
];

const reduceMethod = myArray.reduce((acc, cur, _index, _array) => {
  acc[cur] = acc[cur] ? acc[cur] + 1 : 1;
  return acc;
}, {});

console.log("reduceMethod :>> ", reduceMethod);
// {mango: 2, apple: 1, hash: 1, node: 1}

// how it worked
// firstcall acc => {} , cur => mango, we check acc[cur] but acc is an empty object therefore acc[cur] will be undefined so we set acc[cur] = 1, return  => {mango: 1}

// second call acc => {mango:1}, cur => apple, same as above acc[cur] will be undefine because there acc["apple"] is undefined so we set acc[cur] = 1, return  {mango: 1, apple: 1} 

// thirdcall acc=> {mango: 1, apple: 1} , cur => mango, its a different case here because we already have mango in our acc, so acc[cur] = acc[cur] + 1 => acc[cur] = 2, return {mango: 2, apple: 1} 

// ...
// it continues like that till the last call

Using Reduce to group object

const people = [
  { name: "Alice", age: 21 },
  { name: "Max", age: 20 },
  { name: "Jane", age: 20 },
];
  const grouped = people.reduce((acc, cur) => {
    const key = cur["age"]; // grouping by age value
    const curGroup = acc[key] ?? [];
    acc[key] = [...curGroup, cur];
    return acc
  }, {});

console.log("grouped :>>", grouped)
// grouped :>> {
//    "20": [{"name": "Max","age": 20},{"name": "Jane","age": 20}],
//    "21": [{"name": "Alice","age": 21}]
//     }

Conclusion

Reduce can be powerful but sometimes difficult to understand, and can also make code difficult to understand. In some cases, reduce can be misused without noticing especially when it comes to imitating built-in-functions like the case of example 2 "using reduce to flatten array" we can just say arr.flat() and it will produce the same result.

Note: Reduce often serves one purpose, which is to accumulate an array to a single value. It should be used with caution as it can make code difficult to understand.