Search code examples
arraysd3.jsnestedselectionhierarchical-data

Access nested data without appending in d3.js


I'm having a really hard time understanding how nested selections work. I looked for similar questions but none consider a case in which you access nested data just to set an attribute, without appending anything.

I want to visualize the number of meteorite landings by year in a horizontal bar chart: one rect for each year, with width equal to the number of meteorites fallen that year.

This is the data format (csv):

name,id,nametype,recclass,rectype,mass,fall,year,reclat,reclong,GeoLocation
Renazzo,22586,Valid,CR2,Stony,1000,Fell,1824,44.76667,11.28333,"(44.76667, 11.28333)"
Tounkin,24037,Valid,OC,Stony,2000,Fell,1824,51.73333,102.53333,"(51.73333, 102.53333)"
Zebrak,30397,Valid,H5,Stony,2000,Fell,1824,49.88333,13.91667,"(49.88333, 13.91667)"

In this case, for example, I should get a single bar with length 3.

First thing, I group the data by year through nesting:

var dataByYear = d3.nest()
    .key(function(d) {
        return d.year;
    })
    .entries(data);

I don't use rollup because I need to preserve the values and access them later (I'll need name, nametype, year and mass).

The resulting array looks like this:

enter image description here

Then I draw the rects:

barSvg.selectAll("rect")
    .data(dataByYear).enter().append("rect")
    .attr("x", 10)
    .attr("y", function(d, i) {
        return i * 23;
    })
    .attr("height", 10)
    .selectAll("rect")
    .data(function(d, i) {
        return d;
    }).enter()
    .attr("width", function(d) {
        return d.year.lenght;
    });

The console doesn't display error messages, and all rectangles are drawn but their height attribute doesn't show up.

Can someone tell me what I'm missing here?


Solution

  • First of all, using data() and enter() again in the "enter" selection makes no sense. Get rid of that.

    In your enter selection, each datum has a key property and a values property. The way to get the number of fallen meteorites in that year is simply using values.length.

    Here is a demo with your data (which will render a single rectangle):

    var csv = `name,id,nametype,recclass,rectype,mass,fall,year,reclat,reclong,GeoLocation
    Renazzo,22586,Valid,CR2,Stony,1000,Fell,1824,44.76667,11.28333,"(44.76667, 11.28333)"
    Tounkin,24037,Valid,OC,Stony,2000,Fell,1824,51.73333,102.53333,"(51.73333, 102.53333)"
    Zebrak,30397,Valid,H5,Stony,2000,Fell,1824,49.88333,13.91667,"(49.88333, 13.91667)"`;
    
    var svg = d3.select("svg");
    
    var data = d3.csvParse(csv);
    var dataByYear = d3.nest()
      .key(function(d) {
        return d.year;
      })
      .entries(data);
    
    var rects = svg.selectAll(null)
      .data(dataByYear)
      .enter()
      .append("rect")
      .attr("x", 10)
      .attr("y", function(d, i) {
        return i * 23;
      })
      .attr("height", 10)
      .attr("width", function(d) {
        return d.values.length * 10
      })
    <script src="https://d3js.org/d3.v4.js"></script>
    <svg></svg>