JavaScript reduce and destructuring question

I am currently following James Moore’s Functional programming for beginners with JavaScript course on Udemy. I am having a little trouble understanding how a piece of code works:

const grades = [22, 77, 89, 90, 45, 77, 87, 92, 66, 44, 74, 81, 82, 81, 97];

const gradeCount = grades.reduce(computer, {});

function computer(acc, grade){
  const {a = 0, b = 0, c = 0, d = 0, f = 0} = acc;
  if (grade >= 90){
      return {...acc, a: a + 1};
  } else if (grade >= 80){
      return {...acc, b: b +1};
  } else if (grade >= 70){
      return {...acc, c: c + 1};
  }else if (grade >= 60){
      return {...acc, d: d + 1};
  } else { 
      return {...acc, f: f + 1};
  }
}

console.log(gradeCount);

  1. My first question is; why is destructuring used here as opposed to const a=0, b=0, c=0, d=0, f=0;? This seems far less verbose when compared to the original technique?

  2. Secondly, why is it that the reduce method returns one object containing all the grades with their corresponding quantities as opposed to a separate object for each grade?

Thanks in advance for any advice.

Answer

My first question is; why is destructuring used here as opposed to const a=0, b=0, c=0, d=0, f=0;? This seems far less verbose when compared to the original technique?

If you declare the variables as you suggest, you will not get the previous values from the object:

function original(obj) {
  const { a=0, b=0, c=0, d=0, f=0 } = obj;
  console.log(`const { a=0, b=0, c=0, d=0, f=0 } = obj;
a = ${a}
b = ${b}
c = ${c}
d = ${d}
f = ${f}
`);
}

function proposed(obj) {
  const a=0, b=0, c=0, d=0, f=0;
  
  console.log(`const a=0, b=0, c=0, d=0, f=0;
a = ${a}
b = ${b}
c = ${c}
d = ${d}
f = ${f}
`);
}

const obj = { a: 1, b: 2, d: 4};

original(obj);
proposed(obj);

Destructuring will take the property a from the object on the right side of the = and will assign zero only if it doesn’t find it. So, it’s similar to directly getting the properties:

function explicit(obj) {
  const a = obj.a,
        b = obj.b, 
        c = obj.c, 
        d = obj.d, 
        f = obj.f;
  console.log(`const a = obj.a, b = obj.b, c = obj.c, d = obj.d, f = obj.f;
b = ${b}
c = ${c}
d = ${d}
f = ${f}
`);
}

const obj = { a: 1, b: 2, d: 4};

explicit(obj);

This doesn’t have a fallback to zero just for clarity as to what is happening. A fallback value can be done with the conditional operator ? : which will look like this:

const obj = {b: 2};

const a = obj.a ? obj.a : 0;
const b = obj.b ? obj.b : 0;

console.log(`Using the conditional operator "? :"
a = ${a}
b = ${b}
`)

Alternatively, there is an idiomatic usage of the OR operator || that can also produce a fallback value:

const obj = {b: 2};

const a = obj.a || 0;
const b = obj.b || 0;

console.log(`Using the OR operator "||"
a = ${a}
b = ${b}
`)

These aren’t completely the same as providing a default value in destructuring but at least close enough for illustration of the alternative. Difference is in how falsy values are handled but we can ignore that for now.

So, with this in mind, destructuring is far less verbose than the normal way:

const a = obj.a || 0,
      b = obj.b || 0, 
      c = obj.c || 0, 
      d = obj.d || 0, 
      f = obj.f || 0;

//compared with

const { a=0, b=0, c=0, d=0, f=0 } = obj;

Secondly, why is it that the reduce method returns one object containing all the grades with their corresponding quantities as opposed to a separate object for each grade?

Well, this is how Array#reduce works. I’m going to simplify things a bit and skip over irrelevant details for brevity – feel free to read through the MDN documentation as it’s vastly more thorough than I will be here.

The form of reduce is:

<array>.reduce(callback, initialValue)

Where youu supply it with a callback function that will be called once for each item in the array with two parameters:

function callback(previousResult, currentItem){}
  1. The previous result of callback function. Unless this is the first time it’s called in which case it will use initialValue supplied to .reduce.
    • note – previousResult is very frequently named prev for “previous” or acc for “accumulator”. I chose the long form for clarity but as you can see the one in your code is acc – it is an idiomatic name for this parameter.
  2. The current item being operated on. Items will be visited one by one in sequence.

Quick illustration using a simple reduce to sum all items in the array:

callback = (acc, currentNumber) => acc + currentNumber;
initialValue = 0;
[1, 2, 3].reduce(callback, initialValue);

then the steps .reduce would are illustrated here:

                    [3,   5,   7]
                     ^    ^    ^
----------------------    |    |
acc =           0 |       |    |
currentNumber = 3 |       |    |
result =        3 |       |    |
-------------------       |    |
                          |    |
---------------------------    |
acc =           3 |            |
currentNumber = 5 |            |
result =        8 |            |
-------------------            |
                               |
--------------------------------
acc =           8 |
currentNumber = 7 |
result =        15|
-------------------

The same applies with the code in the question – each time only a single object is produced by the callback, so next time it’s called acc would get a single object again.

Finally, the way the object is updated by using the object spread notation ... to clone and modify a value. {...acc} will create a new object that has the same values as the previous one and {...acc, a: a + 1} will clone it and change the property a to the value of the variable a plus 1. If there was no a property before, then it will be added and since the variable a would be zero, then you’d get { a: 1 }.

const initial = { a: 5};

const obj1 = {...initial, a: 6};
console.log("obj1", obj1);

const obj2 = {...obj1, b: 1};
console.log("obj2", obj2);

const obj3 = {...obj2, b: 2};
console.log("obj3", obj3);
Source: stackoverflow
The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 .