Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to divide a D3 selection into groups by using the selection.filter method in a loop

This is my first post here, so I apologize if it seems a bit clumsy.

I am working on a force directed graph using D3.js. I am encoutering problems with the filter function. Here is my situation : I have created nodes in my graph which have a lot of features. I would like first to be able to create subsets of all those nodes, based on what a user thinks could be a relevant feature and do a visual analysis.

For example, I am working on roses images (which are my nodes), and I would like to filter them depending on their roundness. The roundness feature can go from 0 to 1000, so I first tried to make 5 subsets. The first will have the nodes with a roundness from 0 to 200, the second from 200 to 400, etc... until the last subset which goes from 800 to 1000.

To get the subsets I am using the filter function of d3.js. This is where i have a problem :

note : - the var "nodes" is my set of nodes, they all exist. - The var "groups = []" will contain my five subsets of nodes. - The var "thresholds = []" contains all my thresholds. ex : thresholds[0] == 0, threshold[1] == 200, threshold[2] == 400.

for(i = 0 ;  i < 5 ; i++)                       //going through the creation of 5 subsets
{
    groups[i] = nodes.filter(function(d, i)
    {   
            return d.Shape_Roundness > thresholds[i] && d.Shape_Roundness <= thresholds[i + 1];
            //Get the nodes "d" which have a roundness superior to a minimum threshold and inferior to a maximum threshold
    });
}

When I check the output of my subsets, there is only one result, as if the function breaks when it hits the "return" keyword. What's more the result is always the same in every subset and is the first node of my entire set.

But when I enter hard inputs, by replacing for example "thresholds[i]" and "thresholds[i + 1]" with the numbers 0 and 200 like this :

for(i = 0 ;  i < 5 ; i++)                       //going through the creation of 5 subsets
{
    groups[i] = nodes.filter(function(d, i)
    {   
            return d.Shape_Roundness > 0 && d.Shape_Roundness <= 200;
    });
}

Now, I don't know why but it works, and I have of course 5 identical subsets. I thought that maybe my variable "thresholds = []" does not contain numbers, but when I use the typeof operator on every values, the output is "numbers".

I want to use variables and not hard values because I want it to be possible for the users to change the thresholds. And of course not all features of my nodes go from 0 to 1000.

Can someone tell me what am I doing wrong ?

Thanks a lot, I wish you all a nice day !

like image 545
Evan Avatar asked Mar 01 '26 11:03

Evan


1 Answers

The problem is, that you use the variable i as loopvariable of your for-loop and as parameter of the anonymous function in the filter part.

Inside the anonymous function, only the parameter i can be accessed and your loop variable is invisble because it has the same name. This is called variable shadowing.

As the documentation of D3 explaines here, the second parameter that is given to the callback of the filter function is the index of the node. Therefore inside your callback you access thresholds[0] for the first node of nodes, thresholds[1] for the second node and so on.

Since you don't need the index of the current node for your filter, the easiest fix would be to leave out the i parameter like so:

for(i = 0 ;  i < 5 ; i++) { //going through the creation of 5 subsets 
  groups[i] = nodes.filter(function(d) { 
    return d.Shape_Roundness > thresholds[i] && d.Shape_Roundness <= thresholds[i + 1];
    //Get the nodes "d" which have a roundness superior to a minimum threshold and inferior to a maximum threshold
  });
}

This way the i variable inside the callback will refer to your loop variable.

like image 54
jhinzmann Avatar answered Mar 04 '26 00:03

jhinzmann