简体   繁体   中英

d3: A sub array of objects

I have the following structure:

[
    { 'length': 10, attributes: [1,2,3] },
    { 'length': 7, attributes: [1,3,4,5] },
    { 'length': 12, attributes: [3,5,7,9,10] },
]

 and I am doing the following:


x = d3.scale.linear().domain([0, maxHeight]).range([50, w]),
y = d3.scale.linear().domain([0, maxHeight]).range([h, 20]);
z = d3.scale.linear().domain([0, maxHeight]).range([0, h - 20]);

var chart = svg.selectAll("g.chart")
    .data(items)
    .enter()
    .append("svg:g")
    .attr("class", "chart");

chart.append("svg:rect")
    .attr("fill", 'darkblue')
    .attr("class", 'data')
    .attr("x", function(d, i) { return x(i+1); })
    .attr("y", function(d, i) { return bottom - z(d['length']) + 15 })
    .attr("width", 4)
    .attr("height", function(d, i)  { return z(d['length']) - z(d['min']); })

What I would like to do is add circles on each of these rectangles which corresponds to the attributes in my structure. Basically, (for one 'item'}, I should see something like this:

<g class="chart">
    <rect fill="darkblue" class="data" x="626.1538461538462" y="15" width="6" height="530"></rect>
    <circle cx="626.1538461538462" cy="(y1)" r="5" style="fill: #ffff00; stroke: #808080;"></circle>
    <circle cx="626.1538461538462" cy="(y2)" r="5" style="fill: #ffff00; stroke: #808080;"></circle>
    <circle cx="626.1538461538462" cy="(y3)" r="5" style="fill: #ffff00; stroke: #808080;"></circle>
</g>

The only thing I can think of is looping over the attributes and adding them element by element:

for (z=0; z< 3; ++z)
{
    chart.append("svg:circle")
    .data(items[z]['attributes'])
    .style("fill", 'yellow')
    .style("stroke", "gray")
    .attr("cx", function(d, i) { return x(i+1); })
    .attr("cy", function(d, i) 
    { 
        console.log(d); 
        return bottom - 15;
    })
    .attr("r", 5);
}

Is there a better way to do this?

You can created a nested selection instead of looping:

chart.selectAll("svg:circle")
    .data(function(item) { return item.attributes; })
  .enter()
    .append("svg:circle")
    .style("fill", 'yellow')
    .style("stroke", "gray")
    .attr("cx", function(d, i) { return x(i+1); })
    .attr("cy", function(d, i) 
    { 
        console.log(d); 
        return bottom - 15;
    })
    .attr("r", 5);

Example :

To keep the cx the same for each parent rect , you can pass the parent_idx through

chart.selectAll("svg:circle")
    .data(function(item, parent_idx) { 
        return item.attributes.map(function (attr_val) {
                 return { attr_val: attr_val, parent_idx: parent_idx };
            });
    })
  .enter()
    .append("svg:circle")
    .style("fill", 'yellow')
    .style("stroke", "gray")
    .attr("cx", function(d, i) { return x(d.parent_idx); })
    .attr("cy", function(d, i) 
    { 
        return y(d.attr_val);
    })
    .attr("r", 5);

You can use nested selections. The primary selection will create the groups, each group will have a data item bound to it.

var data = [
    {name: 'A', items: [1, 2]},
    {name: 'B', items: [2, 3, 4]}
];

var cScale = d3.scale.category10()
    .domain(d3.range(10));

var grp = svg.selectAll('g.main')
    .data(data)
    .enter()
    .append('g')
    .attr('class', 'main')
    .attr('transform', function(d, i) {
        return 'translate(0,' + i * 20 + ')';
    });

Then, you can create a nested selection, passing an accessor function to the data method. I have an example with rect elements, but with circles is the same:

grp.selectAll('rect')
    .data(function(d) { return d.items; })
    .enter()
    .append('rect')
    .attr('x', function(d) { return 10 * d; })
    .attr('width', 8)
    .attr('height', 10)
    .attr('fill', function(d) { return cScale(d); });

You may found the article Nested Selections useful. I wrote a small jsfiddle too: http://jsfiddle.net/pnavarrc/h2YVd/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM