0

I have JSON data that looks something like

{'Tree': [
  {"From": "1",
  "To": "2"},
  {"From": "2",
  "To": "3"}
]}

basically an array of all existing references between nodes. I want to be able to draw trees starting from a chosen root node using this data.

I find that using anytree I must link a node to a parent node, but ideally I want to simply link it to the name and have it put together the tree structure on its own.

# I want to do this
child[0] = Node(data[0]["To"], parent=data[0]["From"])

# But I think I need to assign a node, not its name
child[1] = Node(data[1]["To"], parent=child[?])

Suggestions on how to do this?

3
  • Is there any meaning that makes From different from To, or are edges undirected, working on both ways? Commented Feb 1, 2023 at 18:55
  • Is the list guaranteed to be ordered so that no node (other than the root) is referenced via "From" before being referenced via "To"? Commented Feb 1, 2023 at 18:55
  • The edges are directed, from "From" and pointing to "To" nodes. The list is not guaranteed to be ordered. Commented Feb 1, 2023 at 22:49

1 Answer 1

1

Do this in two steps:

  1. Convert the input format into an adjacency list: a dictionary keyed by the node keys, and with associated lists that hold the keys of the connected nodes.

  2. Choose a root node, and then create the anytree tree using the adjacency list.

Here are two functions that deal with these two tasks:

from anytree import Node, RenderTree
from collections import defaultdict 

def makegraph(edges):
    adj = defaultdict(list)
    for edge in edges:
        a, b = edge.values()
        adj[a].append(b)
        adj[b].append(a)
    return adj

def maketree(adj, nodekey):
    visited = set()
    
    def dfs(nodekey, parent):
        if nodekey not in visited:
            visited.add(nodekey)
            node = Node(nodekey, parent)
            for childkey in adj[nodekey]:
                dfs(childkey, node)
            return node
                
    return dfs(nodekey, None)

Here is how to use it:

edges = [
    {"From": "1", "To": "2"},
    {"From": "2", "To": "3"}
]

adj = makegraph(edges)
# Choose "1" as root:
root = maketree(adj, '1')
print(RenderTree(root))
# Now let's choose "2" as root:
root = maketree(adj, '2')
print(RenderTree(root))
Sign up to request clarification or add additional context in comments.

7 Comments

It looks like recursive calls would far exceed the limit and I'll try to convert it to use loops.
Are you sure the input is a tree?
I guess the input is not a tree... In that case you could skip edges that would make a cycle while making the tree. See updated maketree.
You are exactly right, Cycles shouldn't exist in my data but it looks like they do exist. I converted your first ans to use loops and found it was a infinite loop caused by cycles in the structure. Thank you!
The updated function has some issues it looks like 'NoneType' object has no attribute 'children'
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.