I am using d3.nest() in order to make a hierarchical object from a CSV file.
Could you please help me understand why the following code does not work. I didn't manage to use the nesting function within a loop, as described below.
I have the following CSV file, taken from the examples on d3 website:
"type1","type2","type3","type4","type5","size"
"flare","analytics","cluster","AgglomerativeCluster","","3938"
"flare","analytics","cluster","CommunityStructure","","3812"
"flare","analytics","cluster","MergeEdge","","743"
"flare","analytics","graph","BetweennessCentrality","","3534"
"flare","analytics","graph","LinkDistance","","5731"
This basic nesting works:
data = data.entries(csv)
.key(function(d) {return d.type1; })
.key(function(d) {return d.type2; })
.key(function(d) {return d.type3; })
.entries(csv);
I want to use an array of values to specify my keys in order to modify them dynamically.
This works:
var data = d3.nest();
var nesting = ["type1","type2","type3"];
data = data.key(function(d) {return d[nesting[0]]; });
data = data.key(function(d) {return d[nesting[1]]; });
data = data.key(function(d) {return d[nesting[2]]; });
data = data.entries(csv);
But it does not work with a loop...
var data = d3.nest();
for(var i=0;i<nesting.length;i++)
{
data = data.key(function(d) {return d[nesting[i]]; });
}
data = data.entries(csv);
I can't understand why the loop version is not working... Maybe I miss something about the d3.nest() capabilities...
Also, I would like to know if there is a way to "skip" a nesting level if there is nothing filled at this level (ie: the "type5" level on all the lines from the extract above). How could I do that?
Thanks a lot for reading!
This isn't a problem with the .nest()
operator, it's a problem with JavaScript closures. Any time you have this pattern:
for (var x=0; x < y; x++) {
something.attachCallback(function() {
// now do something with x
});
}
You are going to have problems with the closure. The inner anonymous function you're defining doesn't include a copy of the value of x
, it includes a reference to the outer variable x
, which will update when the outer variable updates. So by the end of your loop, the value of x
in every one of your callback functions will be the final value of x
in the loop (in the code above, y
; in your code, nesting.length
).
The D3 .nest()
operator uses its .key()
arguments as callbacks - they aren't executed until you call .map()
or .entries()
. So the problem above applies.
There are various ways to fix this; I tend to use .forEach()
instead of a for
loop. This doesn't work in older browsers, but neither will most of D3, so you're probably safe:
var data = d3.nest();
nesting.forEach(function(key) {
data.key(function(d) {return d[key]; })
});
Another option is to use a separate function for callback definition. Passing the iterator variable to another function will "fix" its value, as the callback now has a reference to the argument of the creator function, not to the original x
variable:
var data = d3.nest();
function addKey(index) {
data.key(function(d) { return d[nesting[index]]; })
}
for(var i=0;i<nesting.length;i++) {
addKey(i);
}
There are a few other approaches as well, but in my opinion forEach
is the most elegant.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With