Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[question] dynamic manipulation of trees #139

Closed
sancelot opened this issue Feb 27, 2019 · 4 comments
Closed

[question] dynamic manipulation of trees #139

sancelot opened this issue Feb 27, 2019 · 4 comments

Comments

@sancelot
Copy link

I am a bit lost when using tree / hierarchy in interactive dynamic context.

Similar to this project I am trying to migrate in v5:
http://bl.ocks.org/robschmuecker/7880033

In a dynamic context, since it should be data driven, I suppose I have to dynamically update my initial data and each time recompute the whole graph ?

It is a bit confusing , because I was thinking that I could modify the tree and the data will be updated automatically, but that does not seem being.

Please can you highlights which way I should use it in a dynamic context.
Thanks.

@nzhorn
Copy link

nzhorn commented Mar 4, 2019

If I understand your question right: yes, it's possible to update hierarchy dynamically.
Let's say that you are loading all children for the expandable node from the server. You are calling load function on click event and then adding response data to the "data" property for the expandable node. After that, you are calling expandNode function and update the chart.

function expandNode(expandableNode) {
   const allChildren = expandableNode.data.children;
   const newHierarchyChildren = [];

   // create array of the nodes that we can add to the tree
   allChildren.forEach((child) => {
     const newNode = d3.hierarchy(child); // create a node
     newNode.depth = expandableNode.depth + 1; // update depth depends on parent
     newNode.height = expandableNode.height;
     newNode.parent = expandableNode; // set parent
     newNode.id = String(child.id); // set uniq id

     newHierarchyChildren.push(newNode);
   });

   // add to the parent's children array and collapse
   expandableNode.children = newHierarchyChildren;
   expandableNode._children = newHierarchyChildren;

   this.update(expandableNode); 
} 

@mading0817
Copy link

If I understand your question right: yes, it's possible to update hierarchy dynamically.
Let's say that you are loading all children for the expandable node from the server. You are calling load function on click event and then adding response data to the "data" property for the expandable node. After that, you are calling expandNode function and update the chart.

function expandNode(expandableNode) {
   const allChildren = expandableNode.data.children;
   const newHierarchyChildren = [];

   // create array of the nodes that we can add to the tree
   allChildren.forEach((child) => {
     const newNode = d3.hierarchy(child); // create a node
     newNode.depth = expandableNode.depth + 1; // update depth depends on parent
     newNode.height = expandableNode.height;
     newNode.parent = expandableNode; // set parent
     newNode.id = String(child.id); // set uniq id

     newHierarchyChildren.push(newNode);
   });

   // add to the parent's children array and collapse
   expandableNode.children = newHierarchyChildren;
   expandableNode._children = newHierarchyChildren;

   this.update(expandableNode); 
} 

Is there any other ways to do it? It shouldn't be a difficult job to add a function like d3.hierarchy.addChildren(index, children)

@mbostock
Copy link
Member

Yeah, d3-hierarchy doesn’t really support mutation of the hierarchy right now, but you can do it “in data” and then reconstruct the tree using d3.hierarchy(data).

It’s fairly easy to detach and re-attach parts of the tree in the same place by manipulating the children array as shown above. But this can easily result in node.height that is inaccurate when the children have been removed, for example.

So, we could have methods like node.add(child), node.remove(child), node.addAll(children) and node.removeAll(), which update the entire hierarchy as appropriate.

That said, I find immutability tends to be cleaner than mutation, so we might think about designing the API to return new trees rather than modifying them in-place. For example, if node.sum returned a copy of the tree, then you could easily pass the same node to multiple hierarchical layouts with different methods of summation and they would co-exist happily. But immutability has a cost, too: by nature of the bidirectional parent-child links you’d then have to copy the entire tree right, and that could be prohibitive for large trees.

At any rate, currently your options are:

  1. Derive an entirely new tree by calling d3.hierarchy after modifying your data (or passing in a different children accessor function to do filtering).

  2. Modify the children of a given node as desired, but remembering to recompute node.height of all ascendants as appropriate, and the like for any other node properties.

@cittadhammo
Copy link

cittadhammo commented Mar 3, 2024

I don't know if this is waterproof, but it looks like you can update the hierarchy with a little trick:

// merge hierarchy B in hierarchy A as child of nodeA

 B.parent = nodeOfA;
 nodeOfA.children.push(B);
 A = d3.hierarchy(A);
 A.each((d) => (d.data = { ...d.data.data, children: d.children }));  // fixes the glitch

See: https://observablehq.com/d/c2df560e9434151d

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

5 participants