7

I have a dynamic array to show a line graph with several lines. Example:

var data = 
[[{x:2005, y:100}, {x:2007, y:96.5}, {x:2009, y:100.3}, {x:2011, y:102.3}], 
 [{x:2005, y:100}, {x:2007, y:105},  {x:2009, y:102},   {x:2011, y:104}]]

This part of my script will draw the lines:

graph.selectAll("path.line")
.data(data)
.enter().append("path")
.attr("class", "line")
.style("stroke", function(d, i) { return d3.rgb(z(i)); })
.style("stroke-width", 2)
.attr("d", d3.svg.line()
.y(function(d) { return y(d.y); })
.x(function(d,i) { return x(i); }));

(The script I'm using is based on http://cgit.drupalcode.org/d3/tree/libraries/d3.linegraph/linegraph.js)

My problem: the data array is dynamic, I don't know beforehand what's in it. Sometimes the y value for 2005 will be null:

var data = 
[[{x:2005, y:100},  {x:2007, y:96.5}, {x:2009, y:100.3}, {x:2011, y:102.3}], 
 [{x:2005, y:null}, {x:2007, y:105},  {x:2009, y:102},   {x:2011, y:104}]]

How can I make the second line ignore the first object, and start at 2007?

Based on answer 1 this is what I have now, still showing the whole line:

data = 
[[{x:2005, y:100},  {x:2007, y:96.5}, {x:2009, y:100.3}, {x:2011, y:102.3}], 
 [{x:2005, y:null}, {x:2007, y:105},  {x:2009, y:102},   {x:2011, y:104}]];

var validatedInput = function(inptArray) { 
 return inptArray.filter(function(obj) {
  return obj.y != null;
 });
};

graph.selectAll("path.line")
    .data(data, validatedInput)
  .enter().append("path")
    .attr("class", "line")
    .style("stroke", function(d, i) { return d3.rgb(z(i)); })
    .style("stroke-width", 2)
    .attr("d", d3.svg.line()
    .y(function(d) { return y(d.y); })
    .x(function(d,i) { return x(i); }));
eindgebruiker
  • 141
  • 1
  • 2
  • 6

2 Answers2

7

In the end I solved this myself, based on the solution here. The trick is to remove the empty values as late as possible, so the positions of all values (points) on the canvas are preserved.

graph.selectAll("path.line")
    .data(data)
  .enter().append("path")
    .attr("class", "line")
    .style("stroke", function(d, i) { return d3.rgb(z(i)); })
    .style("stroke-width", 2)
    .attr("d", d3.svg.line()
    .y(function(d) { return y(d.y); })
    .defined(function(d) { return d.y; }) // Omit empty values.
    .x(function(d,i) { return x(i); }));

This will work for empty values at the start and end of a line.

Community
  • 1
  • 1
eindgebruiker
  • 141
  • 1
  • 2
  • 6
  • Hey @eingebruiker, could you please explain how the line of `defined()` works and let you omit empty values? – user3768495 Nov 18 '15 at 18:04
  • @user3768495 Ugh, that was more than a year ago, and I haven't looked at it since :-) I believe it works like this: It acts as a filter. Look at the last three lines. First the y-position of all points on the canvas is determined. Then, by using defined(), the points where y == null are removed. Then the x-position of the remaining points is determined. – eindgebruiker Nov 20 '15 at 10:59
2

This should do it:

.data(data, function(inptArray) { 
  return inptArray.filter(function(obj) {
   return obj.y != null;
  }) 
});

it would be better though to write it like this:

var validatedInput = function(inptArray) { 
 return inptArray.filter(function(obj) {
  return obj.y != null;
});

.data(data, validatedInput);

Or you can just format your data object before given it to D3:

var data = data.map(function(obj){
 return obj.filter(function(obj) {
  return obj.y != null;
 })
})
sergeyz
  • 1,339
  • 10
  • 14
  • Hi sergeyz, I tried your suggestion, but now the second line is not generated at all. – eindgebruiker Jul 21 '14 at 09:15
  • Do you have two arrays of objects or it's supposed to be one array of objects? Also { x=2005, y=100} means { x: 2005, y: 100}, right? – sergeyz Jul 21 '14 at 13:43
  • In your example you have one array that is assigned to data variable and another array that isn't assigned to anything. One array: var data = [{ x=2005, y=100}, { x=2007, y=96.5}, { x=2009, y=100.3}, { x=2011, y=102.3}] Another arrya: [{ x=2005, y=null}, { x=2007, y=105}, { x=2009, y=102}, { x=2011, y=104}] When you are in DevTool check what is in data right before you pass it to .data. – sergeyz Jul 21 '14 at 13:49
  • Oh sorry, I copied this from a console.log in Firebug, a bit too hastily. data is in fact an array of arrays of objects. I modified my question with the correct data array. – eindgebruiker Jul 21 '14 at 21:19
  • I tried your validateInput (with an extra } that was missing at the end). Now I have two complete lines, including the null value. So it is not working yet. – eindgebruiker Jul 22 '14 at 13:02
  • Strange, it worked on my machine. Try another method. Just added it to my answer. – sergeyz Jul 22 '14 at 14:03
  • Yeah, I tried that last method too. It will indeed skip the first object, but the whole line is moved to the left, so it will start at 2005 and end at 2009. – eindgebruiker Jul 22 '14 at 14:33