TommyF
TommyF

Reputation: 7150

D3 stack() vs nested objects

I'm running into an issue when trying to implement a normalized stacked bar chart using D3v4.
The problem occurs due to my data format which contains nested object arrays populated dynamically on the server side.

var data = [{x:"data1", y:[{name:"red", value:10}, {name:"green", value:20}]},
            {x:"data2", y:[{name:"red", value:30}, {name:"green", value:5}]}];

Calling d3.stack() on this will not work since d3 doesn't know how to traverse into the object array y. (https://jsfiddle.net/xv1qgqjg/)
Is there any way to tell d3.stack() where to find the relevant data similar to the .data(function(d){ return d.y; }) used elsewhere?

Upvotes: 2

Views: 4378

Answers (1)

Gerardo Furtado
Gerardo Furtado

Reputation: 102198

It doesn't seem to be possible. According to the documentation regarding stack(data[, arguments…]),

Any additional arguments are arbitrary; they are simply propagated to accessors along with the this object.

Thus, you'll have to change your data, creating an array which you can pass to d3.stack(), such as this:

[{red:10,green:20},
{red:30,green:5}]

Given the data array in your question, there are several ways for creating the above-mentioned array. Here is my solution (the new array is called newData):

newData = [];

data.forEach(d => {
    var tempObj = {}
    d.y.forEach(e => {
        tempObj[e.name] = e.value;
    })
    newData.push(tempObj);
});

Here is a demo:

var data = [{x:"data1", y:[{name:"red", value:10}, {name:"green", value:20}]},
            {x:"data2", y:[{name:"red", value:30}, {name:"green", value:5}]}];

newData = [];

data.forEach(d => {
    var tempObj = {}
    d.y.forEach(e => {
        tempObj[e.name] = e.value;
    })
    newData.push(tempObj);
});

var stack = d3.stack()
    .keys(["red", "green"])
    .order(d3.stackOrderNone)
    .offset(d3.stackOffsetExpand);

var series = stack(newData);
console.dir(series);
<script src="https://d3js.org/d3.v4.min.js"></script>

Upvotes: 5

Related Questions