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>