Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get one element from d3js selection, by index

Tags:

d3.js

I've created a set of d3js elements based on an array of 3 elements:

var data = [[0,0,2],[0,23,5],[2,12,5]]; circleSet = svg.selectAll()              .data(data)              .enter().append('circle'); 

edit:

How can I select the second element by index?

like image 247
Rich Avatar asked Feb 08 '15 05:02

Rich


2 Answers

The most natural way to manipulate just one element is using the filter function:

var data = [[0,0,2],[0,23,5],[2,12,5]]; var circleSet = svg.selectAll()          .data(data)          .enter()          .append('circle'); var filteredCircleSet = circleSet          .filter(function (d, i) { return i === 1;})          // put all your operations on the second element, e.g.          .append('h1').text('foo');     

Note that depending on what you do with the other elements you might use one of the two variants of this approach:

  • variant a): use the filter in the data function (to reduce the data and the appended elements)

  • variant b): use the filter to exclude instead of to include in order to remove the other elements at the end

See also Filter data in d3 to draw either circle or square

One other way to do it is to use the selection.each method: https://github.com/mbostock/d3/wiki/Selections#wiki-each By using an if statement with the corresponding index you can create a block for one element. E.g.

var data = [[0,0,2],[0,23,5],[2,12,5]]; var circleSet = svg.selectAll()          .data(data)          .enter()          .append('circle')          .each(function (d, i) {             if (i === 1) {               // put all your operations on the second element, e.g.               d3.select(this).append('h1').text(i);                 }           }); 
like image 74
ee2Dev Avatar answered Sep 20 '22 14:09

ee2Dev


In d3 v4 and above, you can use Selection.nodes(). Assuming i is the index number you want:

d3.select(someSelection.nodes()[i]) 

It's a natural one-liner, and it's arguably more readable: you're obviously just getting the node at i in the order, as a D3 selection.


It looks like it'd be more efficient than the alternatives, which involve looping through the entire selection with .each(). So, you might think this is O(1), while the other options are O(n).

Unfortunately, Selection.nodes() itself includes an each loop, so it's also O(n) (not that it's likely to matter in real life unless you call this thousands of times on selections with thousands of nodes):

var nodes = new Array(this.size()), i = -1; this.each(function() { nodes[++i] = this; }); return nodes; 

However, this way you can separate the looping from the getting, which could be useful if efficiency is a major concern.

For example, if you want to loop through each() in selection A and get the item in the same position from selection B, and you want to avoid loops-within-loops because those selections can be huge and you call this many times, you could structure it like this, which would be O(2n) instead of O(n^2):

var selectionBArray = selectionB.nodes()  selectionA.each(function(d, i) {   var iFromSelectionA = this   var iFromSelectionB = d3.select(selectionBArray[i]) }) 

...or if you're using arrow functions to preserve this context:

var selectionBArray = selectionB.nodes()  selectionA.each((d, i, nodes) => {   var iFromSelectionA = d3.select(nodes[i])   var iFromSelectionB = d3.select(selectionBArray[i]) }) 

You could even (ab)use Selection._groups, but I wouldn't recommend using a private property like that since it'll break if a D3 update renamed the _groups property, like this update did.

like image 29
user56reinstatemonica8 Avatar answered Sep 19 '22 14:09

user56reinstatemonica8