William Dickerson
New York, NY, USA
Computer Science, Engineering
& Other Adventures

Nesting Data with D3

June 15th, 2016 | by William Dickerson

The .data(yourData).enter() function is great when you want to logically link some SVG elements to an array of objects. One element to one object. We refer to this as "binding" data. What if each object contains a property nestedData which is itself an array? Maybe you want to have an SVG element for each object in yourData, and also have an SVG element for each value in nestedData.

D3 let's us "nest" our data bindings to accomplish this.

Consider an example where we want to show some stations of the NYC Subway. Here's an array of objects:

var stations = [
    {
        "name": "Times Square",
        "x": 80,
        "y": 100,
        "colors":["red","yellow","purple"]
    },
    {
        "name": "Union Square",
        "x": 100,
        "y": 220,
        "colors":["green","yellow","gray"]
    },
    {
        "name": "Fulton Street",
        "x": 130,
        "y": 320,
        "colors":["blue","red","green","brown"]
    }
]

For simplicity, we're using x and y coordinates instead of latitude and longitude, and we're not worrying about a map of Manhattan.

First, let's prepare an SVG element to draw on:

var plot = d3.select("body")
             .append("svg")
             .attr("width","60vh")
             .attr("height","100vh")
             .attr("viewBox","0 0 350 500");

Now, let's draw a symbol at the location of each station. Our symbol is a white "M" inside a blue square.

// Bind the array of objects
var stationSymbols = plot.selectAll("stations")
                         .data(stations);

// Append an SVG element at the station location
stationSymbols.enter()
              .append("svg")
              .attr("x",function(d){return d.x;})
              .attr("y",function(d){return d.y;})

// Add a blue square for each station
stationSymbols.append("rect")
              .attr("width",30)
              .attr("height",30)
              .attr("fill","skyblue");

// Add an M over each blue square
stationSymbols.append("text")
              .text("M")
              .style("font-size","30px")
              .style("font-family","sans-serif")
              .attr("fill","white")
              .attr("dx",2)
              .attr("dy",26);

So far so good, but now we would like to show the colors (of subway lines) that are served at each station.

// Bind the "colors" array to each station's SVG.
// Remember that "stationSymbols" is already bound to data,
// which gives meaning to "return d.colors;"
var stationColors = 
        stationSymbols.selectAll("stationColors")
                      .data(function(d){return d.colors;});

// Append a square for each color
// Use the index "i" to set the x position.
stationColors.enter()
             .append("rect")
             .attr("width",30)
             .attr("height",30)
             .attr("x",function(d,i){return 32*(i+1)})
             .attr("fill",function(d){console.log(d);return d;});

Here is our example in action:

For more examples of nesting data in D3, see Mike Bostock's explanation here: https://bost.ocks.org/mike/nest/