Combining Parent and Nested Data with d3.js

d3.js, javascript

Solution

You can use closures

var nested = vis
  .selectAll('g')
  .data(data.segments);


nested.enter()
  .append('g')
  .each(function(segment, i) {
    var colors = d3.select(this)
      .selectAll('rect')
      .data(segment.colors);

    colors.enter()
      .append('rect')
      .attr('x', function(color, j) { return pos(segment, j); })
      // OR: .attr('x', function(color, j) { return segment.x + (j * segment.size); })
      .attr('width', function(color, j) { return size(segment); })
      .attr('fill', String);
  });

Problem

I have a data structure like this (assume that the data structure is non-negotiable): ``` data = { segments : [ {x : 20, size : 10, colors : ['#ff0000','#00ff00']}, {x : 40, size : 20, colors : ['#0000ff','#000000']} ]}; ``` Using the d3.js javascript library, I'd like to draw four rectangles, one for each color in both `colors` arrays. Information from each entry in the `segments` array is used to draw the rectangles corresponding to each color in its `color` array. E.g., The red and green rectangles will have a width and height of 10. The resulting html should look like this: ``` <div id="container"> <svg width="200" height="200"> <g> <rect x="20" y="20" width="10" height="10" fill="#ff0000"></rect> <rect x="30" y="30" width="10" height="10" fill="#00ff00"></rect> </g> <g> <rect x="40" y="40" width="20" height="20" fill="#0000ff"></rect> <rect x="60" y="60" width="20" height="20" fill="#000000"></rect> </g> </svg> </div> ``` I've come up with some code that accomplishes this, but I found the part about using data from two different levels of nesting in `data` to be confusing, and I feel that there might be a more idiomatic way to accomplish the same with d3.js. Here's the code (full example at http://jsbin.com/welcome/39650/edit): ``` function pos(d,i) { return d.x + (i * d.size); } // rect position function size(d,i) { return d.size; } // rect size function f(d,i) { return d.color; } // rect color // add the top-level svg element and size it vis = d3 .select('#container') .append('svg') .attr('width',200) .attr('height',200); // add the nested svg elements var nested = vis .selectAll('g') .data(data.segments) .enter() .append('g'); // Add a rectangle for each color nested .selectAll('rect') .data(function(d) { // **** ATTENTION **** // Is there a more idiomatic, d3-ish way to approach this? var expanded = []; for(var i = 0; i < d.colors.length; i++) { expanded.push({ color : d.colors[i], x : d.x size : d.size }); } return expanded; }) .enter() .append('rect') .attr('x',pos) .attr('y',pos) .attr('width',size) .attr('height',size) .attr('fill',f); ``` Is there a better and/or more idiomatic way to access data from two different levels of nesting in a data structure using d3.js? Edit Here's the solution I came up with, thanks to meetamit's answer for the closure idea, and using more idiomatic d3.js indentation thanks to nautat's answer: ``` $(function() { var vis = null, width = 200, height = 200, data = { segments : [ {x : 20, y : 0, size : 10, colors : ['#ff0000','#00ff00']}, {x : 40, y : 0, size : 20, colors : ['#0000ff','#000000']} ] }; // set the color function f(d,i) {return d;} // set the position function pos(segment) { return function(d,i) { return segment.x + (i * segment.size); }; } // set the size function size(segment) { return function() { return segment.size; }; } // add the top-level svg element and size it vis = d3.select('#container').append('svg') .attr('width',width) .attr('height',height); // add the nested svg elements var nested = vis .selectAll('g') .data(data.segments) .enter().append('g'); // Add a rectangle for each color. Size of rectangles is determined // by the "parent" data object. nested .each(function(segment, i) { var ps = pos(segment), sz = size(segment); var colors = d3.select(this) .selectAll('rect') .data(segment.colors) .enter().append('rect') .attr('x', ps) .attr('y',ps) .attr('width', sz) .attr('height',sz) .attr('fill', f); }); }); ``` Here's the full working example: http://jsbin.com/welcome/42885/edit

Original source