'How to print tree from dictionary in python

I am trying to implement ldd kind of application in python. However, I am struggling to represent the data as a tree like structure. The linux libraries are dependent on some other libraries, so my task is to track the dependency list until no dependency is found. I have data in dictionary shown below,

{lib_A: [lib_1, lib_2, lib_3, lib_4], lib_1: [lib_x, lib_y, lib_z], lib_x: [lib_i], lib_y: [lib_p], lib_2: [lib_a, lib_b], lib_a: [lib_11]......}

where lib_1, lib_2, lib_3, and lib_4 depends on lib_A. Then lib_x, lib_y, and lib_z depends on lib_1 and so on. The list continues until no dependency is found. If I print the data in a for loop the output looks like this as shown below,

for lib, dep_lib in dependency_dict.items():
    print(lib, dep_lib)
lib_A [lib_1, lib_2, lib_3, lib_4]
lib_1 [lib_x, lib_y, lib_z]
lib_x [lib_i]
lib_y [lib_p]
lib_2 [lib_a, lib_b]
lib_a [lib_11]
...
..

and I want to represent the above data as a tree like structure as shown below.

lib_A <= lib_1
         <= lib_x
            <= lib_i
         <= lib_y
            <= lib_p
         <= lib_z
      <= lib_2
         <= lib_a
            <= lib_11
         <= lib_b
      <= lib_3
      <= lib_4




Solution 1:[1]

When dealing with a tree based structure, it's often natural to use a recursive function to process it. To produce the output you want, I'd write something like this:

def process_lib(data, lib, padding=0):
    if padding == 0:
        print(lib)
    else:
        print(" "*padding, "<=", lib)

    for child in data.get(lib, []): # base case: lib is not in the dict
        process_lib(data, child, padding + 3)  # recursive case

This doesn't exactly replicate the first line of your example, since we put lib_A on its own line with lib_1 on the next line. You could put in more special case logic for the padding == 0 case and figure out how to make the first child directly follow it, but hopefully this output is good enough:

lib_A
    <= lib_1
       <= lib_x
          <= lib_i
       <= lib_y
          <= lib_p
       <= lib_z
    <= lib_2
       <= lib_a
          <= lib_11
       <= lib_b
    <= lib_3
    <= lib_4

This code probably only works properly for simple trees. If your real data is a more complicated graph with the same libraries showing up at multiple points in the dependency hierarchy, then you need something a little more complicated. You need to keep track of which nodes have already been seen, and either not print them out again at all, or print them but not their dependencies. Here's an implementation that does the latter:

def process_lib(data, lib, padding=0, seen=None):
    if seen is None:
        seen = set()
    if padding == 0:
        print(lib)
    else:
        print(" "*padding, "<=", lib)

    if lib in seen:
        print(" "*(padding+3), "<= ...") # alternate base case
    else:
        seen.add(lib)
        for child in data.get(lib, []):
            process_lib(data, child, padding + 3, seen)

If you don't want duplicates at all, use this:

def process_lib(data, lib, padding=0, seen=None):
    if seen is None:
        seen = set()
    if padding == 0:
        print(lib)
    else:
        print(" "*padding, "<=", lib)

    seen.add(lib)
    for child in data.get(lib, []):
        if child not in seen:
            process_lib(data, child, padding + 3, seen)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1