Juntra
Juntra

Reputation: 119

d3js roll up a nested JSON array and sum up a total value

I have this JSON

[{
    "month": "september",
    "detail": [{
        "date": "01-09",
        "value": 5
    }, {
        "date": "02-09",
        "value": 5
    }, {
        "date": "03-09",
        "value": 5
    }, {
        "date": "04-09",
        "value": 5
    }, {
        "date": "05-09",
        "value": 5
    }, {
        "date": "06-09",
        "value": 5
    }, {
        "date": "07-09",
        "value": 0
    }]
},

{
    "month": "october",
    "detail": [{
        "date": "01-10",
        "value": 10
    }, {
        "date": "02-10",
        "value": 5
    }, {
        "date": "03-10",
        "value": 5
    }, {
        "date": "04-10",
        "value": 5
    }, {
        "date": "05-10",
        "value": 5
    }, {
        "date": "07-10",
        "value": 10
    }]
}

I want to roll up the all the "value" in the object "detail" for each specific month using d3nest. If you count all the values for each specific months it would result in: september-value: 30 & october-value: 40.

I've tried nesting it but I can't get it right to sum up the values for each month. see my code.

d3.json('runningdata.json', function (error, data) {
console.log(data);

var Total = d3.nest()
.key(function(d) { return d.month; })
.rollup(function(value) { return d3.sum(value, function(v) { return v.detail.value; }); })

.entries(data);
console.log(JSON.stringify(Total));
});

All of this above would result in:

[{"key":"september","values":0},{"key":"october","values":0}]

You can notice the "key" are working right, they have the month as value. but the "Values" field result in 0. what i am trying to achieve needs to be:

[{"key":"september","values":30},{"key":"october","values":40}]

But when i try this:

.rollup(function(value) { return d3.sum(value, function(v) { return v.detail[0].value; }); })

instead of:

.rollup(function(value) { return d3.sum(value, function(v) { return v.detail.value; }); })

it shows me the values of the first object in the arrays.

[{"key":"september","values":5},{"key":"october","values":10}]

What am i doing wrong? I have been reading about d3 nesting. Any help is welcome.

Note: Im trying achieve this to make a graph which presents the total of each months and when you click on the specific month it will view the details of its month in days.

Upvotes: 0

Views: 1337

Answers (2)

mhodges
mhodges

Reputation: 11116

You can just use basic javascript .reduce() function to do the trick. This will roll up your array of object structure into a single object with the months as the property name and the sum of the values as the value. The advantage to doing it this way is that if you want to get, say, the total for the month of September, you can simply say combined.september, rather than having to iterate through an array, searching for the object with the key property equal to "september" and then extracting the value. However, if you need to keep your original structure, see the slightly modified version at the bottom of my answer.

var data = [{
    "month": "september",
    "detail": [{
        "date": "01-09",
        "value": 5
    }, {
        "date": "02-09",
        "value": 5
    }, {
        "date": "03-09",
        "value": 5
    }, {
        "date": "04-09",
        "value": 5
    }, {
        "date": "05-09",
        "value": 5
    }, {
        "date": "06-09",
        "value": 5
    }, {
        "date": "07-09",
        "value": 0
    }]
},

{
    "month": "october",
    "detail": [{
        "date": "01-10",
        "value": 10
    }, {
        "date": "02-10",
        "value": 5
    }, {
        "date": "03-10",
        "value": 5
    }, {
        "date": "04-10",
        "value": 5
    }, {
        "date": "05-10",
        "value": 5
    }, {
        "date": "07-10",
        "value": 10
    }]
}];

var combined = data.reduce(function (total, current){
  total[current.month] = current.detail.reduce(function(tot, curr) {
    return tot + curr.value;
  }, 0);
  return total;
}, {});

console.log(combined);

Edit -- if you need to keep your original {"key": "..", "values", ".."} structure

You can use .map() rather than .reduce() like so:

var combined = data.map(function (elem){
  var total = elem.detail.reduce(function(total, current) {
    return total + current.value;
  }, 0);
  return {key: elem.month, values: total};
});

console.log(combined); 

// output:
// [ { key: 'september', value: 30 },
//   { key: 'october', value: 40 } ]

Upvotes: 1

Kapil Kashyap
Kapil Kashyap

Reputation: 111

You have to update your d3.sum function like so:

d3.sum(value[0].detail, function(v) {
   return v.value;
});

So your whole code would be:

var Total = d3.nest()
.key(function(d) {
    return d.month;
})
.rollup(function(value) {
    return d3.sum(value[0].detail, function(v) {
        return v.value;
    });
})
.entries(data);

Upvotes: 2

Related Questions