Reputation: 4639
I was looking at another stackoverflow question and I tried the following:
d3.selectAll(links.filter(function(db) {
return db.source.id == 'foo'
})).each(function(p) {
console.log (p.source.id)
})
And found that it returned with a
TypeError: Cannot read property 'source' of undefined
even though the filtered selection comes back as a proper array of objects with .source.id values (this example uses the standard link notation found in D3's force-directed networks).
I'm just curious as to why this wouldn't work.
Upvotes: 3
Views: 27841
Reputation: 5233
Make sure you are clear on which of the two following methods you are using:
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/filter https://github.com/mbostock/d3/wiki/Selections#wiki-filter
The d3.selectAll function accepts a selector string or an array of DOM nodes. Which one is it in your case?
https://github.com/mbostock/d3/wiki/Selections#wiki-d3_selectAll
Remember that the variable p in the selection.each() callback function corresponds to the datum bound to the elements in the selection.
https://github.com/mbostock/d3/wiki/Selections#wiki-each
UPDATE
Your links variable seems to be an array of regular JavaScript objects. This is not correct because the d3.selectAll
function expects an array of DOM nodes (or a selector string). Interestingly, it will not complain if you use a regular array as argument; for example, you can still invoke the selection.each
method:
selection.each(function)
Invokes the specified function for each element in the current selection, passing in the current datum d and index i, with the this context of the current DOM element. This operator is used internally by nearly every other operator, and can be used to invoke arbitrary code for each selected element. The each operator can be used to process selections recursively, by using d3.select(this) within the callback function.
However, because the selection is not a real selection of DOM nodes with data bound to them, you see that the first argument in the function (normally d, in your case p) will be undefined.
The second argument, the index i, will still correspond to the index of the original array that we are iterating over. That is the reason why d3.selectAll(links).each(function(p, i) { console.log(links[i].source.id); })
worked for you. It is basically doing the same as this (non-d3) JavaScript expression: links.forEach(function(v, i) { console.log(links[i].source.id); })
One more simplified example of what you were looking at:
// anti-pattern:
var arr = ['a', 'b', 'c'];
d3.selectAll(arr)
.each(function(d, i) {
console.log(d, i, arr[i]);
});
Which logs to the console:
undefined 0 "a"
undefined 1 "b"
undefined 2 "c"
So, instead, if you're trying to inspect the links in a force-directed layout you could select the DOM nodes that represent those links. Take the standard force example included in the D3 library: http://d3-example.herokuapp.com/examples/force/force.htm and run the following from the console:
d3.selectAll('line')
.each(function(d, i) {
console.log(d.source.index);
});
Upvotes: 7