D3.js: access data is nested 2 levels down

Data structure:

var data = [ {name: "male", values: [ { count: 12345, date: Date 2015-xxx, name: "male" }, {...} ] }, {name: "female", values: [ { count: 6789, date: Date 2015-xxx, name: "female" }, {...} ] } ] 

The values ​​I want to get are the data [a] .values ​​[b] .count

The values ​​are used to draw circles for my graph.

code for circle graph:

 focus.selectAll(".dot") .data(data) .enter().append("circle") .attr("class", "dot") .attr("cx", function(d,i) { return x(d.values[i].date); }) .attr("cy", function(d,i) { return y(d.values[i].count); }) .attr("r", 4) .style("fill", function(d,i) { return color(d.values[i].name); }) 

The problem is that i = 1 due to its position in the object.

What I want to do is skip all objects under values . How can i do this?

Edit: I want to learn how to do this without modifying the data to improve my skills.

Thanks.

+5
source share
2 answers

The easiest way is to use lib as underscore.js to edit your data array.

From underscore documents:

flatten _.flatten (array, [shallow]) Smoothes the nested array (nesting can be at any depth). If you go shallow,> the array will only be flattened one level.

 _.flatten([1, [2], [3, [[4]]]]); -> [1, 2, 3, 4]; _.flatten([1, [2], [3, [[4]]]], true); -> [1, 2, 3, [[4]]]; 

map _.map (list, iteration, [context]) Alias: collect Creates a new array of values, matching each value in the list through the conversion function (iteratee). Three arguments are passed to the iterator: the value>, then the index (or key) of the iteration and, finally, the list link> whole.

 _.map([1, 2, 3], function(num){ return num * 3; }); => [3, 6, 9] _.map({one: 1, two: 2, three: 3}, function(num, key){ return num * 3; }); => [3, 6, 9] _.map([[1, 2], [3, 4]], _.first); => [1, 3] 

Underline documentation

In code, you can do something like this:

 var flatData = _.flatten(_.map(data, (d)=>d.values)); focus.selectAll(".dot") .data(data) .enter().append("circle") .attr("class", "dot") .attr("cx", function(d,i) { return x(d.date); }) .attr("cy", function(d,i) { return y(d.count); }) .attr("r", 4) .style("fill", function(d,i) { return color(d.name); }) 
+4
source

There are several ways to do what you want, only using D3, without any other library and without changing the data. One of them uses groups to handle β€œhigher” data levels (relative to nested data). See it in this code:

First, I calculated the dataset just like yours:

 var data = [ {name: "male", values: [{ x: 123,y: 234}, { x: 432,y: 221}, { x: 199,y: 56}] }, {name: "female", values: [{ x: 223,y: 111}, { x: 67,y: 288}, { x: 19, y: 387}] } ]; 

This is the data we are going to use. I'm going to make a scatter chart here (as an example), so let me set the domains for the scale by referring to the second level of data ( x and y inside values ):

 var xScale = d3.scaleLinear().range([20, 380]) .domain([0, d3.max(data, function(d){ return d3.max(d.values, function(d){ return dx; }) })]); var yScale = d3.scaleLinear().range([20, 380]) .domain([0, d3.max(data, function(d){ return d3.max(d.values, function(d){ return dy; }) })]); 

Now comes the most important part: we are going to bind the data to the "groups", and not to the circle elements:

 var circlesGroups = svg.selectAll(".circlesGroups") .data(data) .enter() .append("g") .attr("fill", function(d){ return (d.name == "male") ? "blue" : "red"}); 

As soon as we have 2 objects at the first data level, D3 will create 2 groups for us.

I also used groups to set the colors of the circles: if name is male, the circle is blue, otherwise it is red:

 .attr("fill", function(d){ return (d.name == "male") ? "blue" : "red"}); 

Now, having created the groups, we create circles according to the values in the data of each group, binding the data as follows:

  var circles = circlesGroups.selectAll(".circles") .data(function(d){ return d.values}) .enter() .append("circle"); 

Here function(d){ return d.values} will connect the data with the circles according to the objects inside the values arrays.

And then you arrange your circles. This is all the code, click β€œrun snippet of code” to see it:

 var data = [ {name: "male", values: [{ x: 123,y: 234}, { x: 432,y: 221}, { x: 199,y: 56}] }, {name: "female", values: [{ x: 223,y: 111}, { x: 67,y: 288}, { x: 19, y: 387}] } ]; var xScale = d3.scaleLinear().range([20, 380]) .domain([0, d3.max(data, function(d){ return d3.max(d.values, function(d){ return dx; }) })]); var yScale = d3.scaleLinear().range([20, 380]) .domain([0, d3.max(data, function(d){ return d3.max(d.values, function(d){ return dy; }) })]); var xAxis = d3.axisBottom(xScale).tickSizeInner(-360); var yAxis = d3.axisLeft(yScale).tickSizeInner(-360); var svg = d3.select("body") .append("svg") .attr("width", 400) .attr("height", 400); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0,380)") .call(xAxis); svg.append("g") .attr("class", "y axis") .attr("transform", "translate(20,0)") .call(yAxis); var circlesGroups = svg.selectAll(".circlesGroups") .data(data) .enter() .append("g") .attr("fill", function(d){ return (d.name == "male") ? "blue" : "red"}); var circles = circlesGroups.selectAll(".circles") .data(function(d){ return d.values}) .enter() .append("circle"); circles.attr("r", 10) .attr("cx", function(d){ return xScale(dx)}) .attr("cy", function(d){ return yScale(dy)}); 
 .axis path, line{ stroke: gainsboro; } 
 <script src="https://d3js.org/d3.v4.min.js"></script> 
+1
source

All Articles