make sure d3 data element matches id? - d3.js

I've got an existing svg with a bunch of polygons. Each polygon has a unique id, and with each id, I have some associated data.
My question is this: If I do the natural d3 join:
d3.selectAll("polygon").data(array_of_data)
Is there anyway to insure that the data element associated with a polygon is the correct one?
Or do I just need to keep the order of the array_of_data the same as the order of the selected polygons?

D3's data() function takes an optional second argument that is intended to provide just such a correspondence between datum and DOM nodes. Try this:
d3.selectAll('polygon').data(array_of_data, function(d) { return d.id; });
That second argument to data() is a callback function that, when called with a datum, returns the key that binds each DOM node to it's corresponding datum. When you don't provide such a function, D3 is left with no option but to use the index to bind datum to DOM nodes.

Related

click in datatable to filter other charts (dc.js)

I need to filter other charts when I click a row in the datatable.
I did
my_table.on('pretransition', function (table) {
table.selectAll('td.dc-table-column')
.on('click',function(d){
table.filter(d.key)
dc.redrawAll();
})
});
but nothing happens in the other charts.
Can you help me, please?
If the table dimension is a dimension...
The data that ordinarily populates a data table is the raw rows from the original data set, not key/value pairs.
So it is likely that d.key is undefined.
I'd advise you first to stick
console.log(d)
into your click handler to see what your data looks like, to make sure d.key is valid.
Second, remember that a chart filters through its dimension. So you will need to pass a value to table.filter() that is a valid key for your dimension, and then it will filter out all rows for which the key is different. This may not be just the one row that you chose.
Typically a table dimension is chosen for the way it orders the values for the rows. You might actually want to filter some other dimension. But hopefully this is enough to get you started.
But what if the the table dimension is a group?
The above technique will only work if your table takes a crossfilter dimension as its dimension. If, as in the fiddle you linked in the comments, you're using a group as your dimension, that object has no .filter() method, so the table.filter() method won't do anything.
If you only need to filter the one item that was clicked, you could just do
foodim.filter(d.key)
This has an effect but it's not that useful.
If you need the toggle functionality used in dc's ordinal charts, you'll need to simulate it. It's not all that complicated:
// global
var filterKeys = [];
// inside click event
if(filterKeys.indexOf(d.key)===-1)
filterKeys.push(d.key);
else
filterKeys = filterKeys.filter(k => k != d.key);
if(filterKeys.length === 0)
foodim.filter(null);
else
foodim.filterFunction(function(d) {
return filterKeys.indexOf(d) !== -1;
})
Example fiddle: https://jsfiddle.net/gordonwoodhull/kfmfkLj0/9/

Using fields in inherited bound data

I am attempting to get my head around using bound data with d3.js. I'm following the documentation and am now a little confused.
I want to produce donut charts with radii that vary depending on the data. I am comfortable producing the arcs to make up a donut using an array, but am having a hard time working out how to pass along a size parameter with the data binding of the arc. For example, if the data bound to the parent of the arc is something like {size: 20, cont: [1, 7]}, how can I bind the first element of the array as well as the size element? I have a fiddle attempting to show what I am talking about. In that example, the two donuts should be different sizes. I have commented out the kind of thing I suspect should be going on on line 14.
I have tried variations on:
var arcs = donuts.selectAll(".arc")
.data(function(d) { var temp = [];
temp.push(d.cont);
temp.push(d.size);
return temp; })
.enter()
.append("g")
.attr("class", "arc");
But it is clearly not producing what I expect.
The problem here isn't really the data inheritance, but the fact that you're passing the original data to a layout and then only the result of that to your drawing functions. The pie layout does store the original datum in the .data member of the result, but you're only passing it part of the original data.
The "proper" thing to do would be to refactor your data structure such that you can pass it in as-is and use the pie layout's .value() function to tell it how to access the data. Then you can directly access the original data.
There's however a quicker solution -- you can simply use the indices that are passed to your function to index into the original array. The code for this would look like this.
.attr("d", function(d, i, j) { return arc.outerRadius(dataset[j].size)(d); })
Note that you need two indices here because you have nested data -- i would be the index within your array of values for a single pie chart, whereas j denotes the index of the element at the level above that. Updated jsfiddle here.

Calling a function once when data enters in d3

Is there a way to call a function when data enters using data().enter()?
For example, in this current jsfiddle: http://jsfiddle.net/p3m8A/4/ , I have a function that draws a group and I want to call this function when new data enters. The current jsfiddle doesn't do anything but the objective is to click on the red square and using .data.enter draw a purple square when the red square is clicked.
The specific part I'm trying to get to work is :
canvas.selectAll("#boxGroup")
.data(data)
.enter().function(d,i) {
drawBox(150,20,d);
};
Thanks
The callback passed to the call method is actually passed the selection, not the data.
enterSelection.call(function(selection){/* this === selection */});
So, what you were probably looking for is the each method.
enterSelection.each(function(d, i){/* this is selection */ drawBoxes(150,20,d);});
You want the method .call(function(d))
This will run your function once, passing d as the array of all of the data you have provided. i is not defined for using call after enter().
If you want to draw multiple boxes, based on d, your code would look something like this:
canvas.selectAll("boxGroup")
.data(data)
.enter()
.call(function(d){drawBoxes(150,20,d);});
I've created a basic fiddle of this here.
Note that this is what you want to use if you want to call a function on the selection returned by .enter() in the same spot as you're using it. It's also possible to bind a function to the enter event of a given DOM element by using .on('enter',function), but this would require that the element that you are entering data into already exist.

access current attributes in d3 functions

circle is an array of four d3 circles.
circle
.attr( "cy", function(){ this.attr("cy") + 10*input_data.pitch });
This fails. How can I access the individual attributes in anonymous functions like above?
Inside your function, this is an Element per the W3C DOM API. So it’s just this.getAttribute("cy").
Two more things: you forgot to return a value. And since attribute values are strings, you’ll need to coerce them to number before you can add another number. Otherwise your number will get coerced to a string and then the two strings will be concatenated: "10" + 2 is "102", not 12.
So, like this:
circle.attr("cy", function() {
return +this.getAttribute("cy") + 10 * input_data.pitch;
});
All that said, it’s generally not idiomatic D3 to pull data back out of DOM attributes. (It’s slow and you have coercion and serialization problems since DOM attributes can only be strings.) So I would recommend looking for a way to do this based on data, and limit yourself to data-driven documents… not document-driven data!

How data in a d3 scatterplot could be referenced/called outside d3 selection

I have a d3 scatterplot and want to know how to get the value of the data appended to one single record (circle) in order to call it later in the code.
Say we have the following data:
ID X Y
A 1 1
B 2 2
C 3 3
How can I get the X value of the record B?
Does anyone have an idea how to do it?
thx
You may need to show more code, as it depends on how you're attaching the data. But assuming that you have something like
var circle = svg.selectAll("circle").data(myArrayOfDataObjects)
.enter().append("circle");
You can assign an ID or class to the DOM element you create:
circle.attr("id", function(d) { return "dataRow" + d.ID; });
Now you can use d3, plain JS, or the library of your choice to get a handle on the DOM element later on. D3 attaches its data as a __data__ property on the DOM element, so you can reference that to get your datum. D3 also provides the .datum() method to get this value:
var myDataRow = d3.selectAll("#dataRowB").datum();
var xValue = myDataRow.X;
Or, in plain JavaScript:
var myDataRow = document.getElementById("#dataRowB").__data__;
// etc
If jQuery is an option, in your d3 you can use the jQuery .data() function to attach the d3 data directly to the SVG element. Simply run something like .each(function(d){ $(this).data(d) }).
You can then access the data anywhere by using $(selector).data(), where selector points to the DOM element or elements that you're interested in, with something like '#RecordB'.
If your data is more complex, (say, d = {foo: 'foo', bar: 'bar'}), you'd use $(selector).data()[foo] to grab whatever data is keyed to 'foo' in the data associated with whatever is pointed at by the selector.

Resources