Trying to use .reduce on an object

Viewed 81

So I have an object that I need to sum a nested value for all the objects. My object looks like

const json = [
{
    "other_sum": "1",
    "summary": {
        "calculations": {
            "time": 10,
            "unit": 25
        },
        "updated": "2020-06-05"
    }
},
{
    "other_sum": "1",
    "summary": {
        "calculations": {
            "time": 20,
            "unit": 5
        },
        "updated": "2020-06-05"
    }
},
{
    "other_sum": "1",
    "summary": {
        "calculations": {
            "time": 5,
            "unit": 15
        },
        "updated": "2020-06-05"
    }
},
];

I need to sum all the "unit" values

I've been trying to use .reduce for this but it only works when I have 2 items in my object, onces I get a 3rd I get an error.

The rest of my code is the following:

const r = json.reduce((a, b) => a.summary.calculations.unit + b.summary.calculations.unit);
console.log(r);

Not really sure what I'm doing wrong atm.

4 Answers

Because thats not the way reduce works. Taken from this article it works like this:

Given

arrSum = function(arr){
  return arr.reduce(function(a,b){
    return a + b
  }, 0);
}

The function that we pass as the first parameter of the reduce method receives two parameters, a and b. In this code, a is our accumulator. It will accumulate our sum as our function works. b is the current value being processed.

So basically you are trying to access "summary" in the accumulator which is only a number. Here is the working snippet:

json = [
{
    "other_sum": "1",
    "summary": {
        "calculations": {
            "time": 10,
            "unit": 25
        },
        "updated": "2020-06-05"
    }
},
{
    "other_sum": "1",
    "summary": {
        "calculations": {
            "time": 20,
            "unit": 5
        },
        "updated": "2020-06-05"
    }
},
{
    "other_sum": "1",
    "summary": {
        "calculations": {
            "time": 5,
            "unit": 15
        },
        "updated": "2020-06-05"
    }
},
];

const r = json.reduce((a, b) => a + b.summary.calculations.unit, 0);
console.log(r);

The first argument to the callback passed to .reduce is the accumulator, and the value returned from each iteration becomes the accumulator for the next iteration.

Therefore, the value called on the next item is going to be a.summary.calculations.unit + b.summary.calculations.unit, and the next time around, your callback will look for the nested property a.summary.calculations.unit on that returned value, which presumably does not exist.

You may want to change it to something along the lines of:

const r = json.reduce((a, b) => a + b.summary.calculations.unit, 0);

where 0 is the initial value for your accumulator.

MDN, as always, is a good reference.

The first value of the reduce function is not an object but the previous value that you are returned. You can change your code to this.

 json.reduce((total, b) => total+ b.summary.calculations.unit, 0);

You could use my dig function:

function dig(obj, func){
  let v;
  if(obj instanceof Array){
    for(let i=0,l=obj.length; i<l; i++){
      v = obj[i]; func(v, i, obj);
      if(typeof v === 'object')dig(v, func);
    }
  }
  else{
    for(let i in obj){
      v = obj[i]; func(v, i, obj);
      if(typeof v === 'object')dig(v, func);
    }
  }
}
const json = [
  {
    "other_sum": "1",
    "summary": {
      "calculations": {
          "time": 10,
          "unit": 25
      },
      "updated": "2020-06-05"
    }
  },
  {
    "other_sum": "1",
    "summary": {
      "calculations": {
        "time": 20,
         "unit": 5
      },
      "updated": "2020-06-05"
    }
  },
  {
    "other_sum": "1",
    "summary": {
      "calculations": {
        "time": 5,
        "unit": 15
      },
      "updated": "2020-06-05"
    }
  }
];
let unitTotal = 0, timeTotal = 0;
dig(json, (v, k)=>{
  if(k === 'unit'){
    unitTotal += v;
  }
  else if(k === 'time'){
    timeTotal += v;
  }
});
console.log('unitTotal: '+unitTotal);
console.log('timeTotal: '+timeTotal);

Related