Home > other >  Don't indent first level of tree in a recursive tree printer
Don't indent first level of tree in a recursive tree printer

Time:11-18

I would like to refactor a recursive tree-printing function I wrote so that the root node, the first call, is not indented at all.

Tree = dict[str, 'Tree']
def print_tree(tree: Tree, prefix: str=''):
    if not tree:
        return
    markers = [('├── ', '│   '), ('└── ', '    ')]
    children = list(tree.items())
    for key, subtree in children:
        is_last_child = (key, subtree) == children[-1]
        key_prefix, subtree_prefix = markers[is_last_child]
        print(prefix   key_prefix   key)
        print_tree(subtree, prefix   subtree_prefix)

tree = {'.': {'alpha':{}, 'beta': {'beta.alpha':{}, 'beta.beta':{}}, 'charlie': {'charlie.alpha':{}, 'charlie.beta':{}, 'charlie.charlie':{}}, 'delta':{}}}
print_tree(tree)

Current output is

└── .
    ├── alpha
    ├── beta
    │   ├── beta.alpha
    │   └── beta.beta
    ├── charlie
    │   ├── charlie.alpha
    │   ├── charlie.beta
    │   └── charlie.charlie
    └── delta

But I would like the output to be

.
├── alpha
├── beta
│   ├── beta.alpha
│   └── beta.beta
├── charlie
│   ├── charlie.alpha
│   ├── charlie.beta
│   └── charlie.charlie
└── delta

I can't think of a way to do it elegantly, as in without special-casing the first call like this:

def print_tree(tree: Tree, prefix: str='', root: bool=True):
    if not tree:
        return
    markers = [('├── ', '│   '), ('└── ', '    ')]
    if root:
        markers = [('', ''), ('', '')]
    children = list(tree.items())
    for key, subtree in children:
        is_last_child = (key, subtree) == children[-1]
        key_prefix, subtree_prefix = markers[is_last_child]
        print(prefix   key_prefix   key)
        print_tree(subtree, prefix   subtree_prefix, root=False)

How can I change the way I'm recursing to accomplish this? I don't want to add an extra argument to the function or otherwise require more information about state. I like how simple my current function is, it just prefixes the first level when I don't really want it to.

CodePudding user response:

Beautiful algorithm, I like it! (Much better than mine)

This is the best I could do with your given restraints:

Tree = dict[str, 'Tree']
def print_tree(tree: Tree, prefix: str=None):
    markers = [('├── ', '│   '), ('└── ', '    '), ('', '')]
    for i, (key, subtree) in enumerate(tree.items()):
        is_last_child = i == len(tree) - 1
        marker_index = 2 if prefix is None else is_last_child
        prefix = prefix or ""
        key_prefix, subtree_prefix = markers[marker_index]
        print(prefix   key_prefix   key)
        print_tree(subtree, prefix   subtree_prefix)

tree = {'.': {'alpha':{}, 'beta': {'beta.alpha':{}, 'beta.beta':{}}, 'charlie': {'charlie.alpha':{}, 'charlie.beta':{}, 'charlie.charlie':{}}, 'delta':{}}}
print_tree(tree)
.
├── alpha
├── beta
│   ├── beta.alpha
│   └── beta.beta
├── charlie
│   ├── charlie.alpha
│   ├── charlie.beta
│   └── charlie.charlie
└── delta
  • Storing the root indicator inside the prefix by making the default None
  • Added a third marker option which only root uses
  • Replaced children list with enumerate using i to check if it's the last child
  • Removed unnecessary empty tree check

CodePudding user response:

It does not yet do what you want, but you can maybe tweak it.

Basically, I've just added a level parameter to track where you are at. It defaults to 0 so your initial call is much the same.

Tree = dict[str, 'Tree']
def print_tree(tree: Tree, prefix: str='',level=0):
    # level starts out at zero....
    if not tree:
        return
    markers = [('├── ', '│   '), ('└── ', '    ')]
    children = list(tree.items())
    for key, subtree in children:
        is_last_child = (key, subtree) == children[-1]
        key_prefix, subtree_prefix = markers[is_last_child]

        #only if on level 1 
        if level:
            #hack to avoid the extra blanks on the left
            print((prefix   key_prefix   key).lstrip())
        print_tree(subtree, prefix   subtree_prefix,level=level 1)


tree = {'.': {'alpha':{}, 'beta': {'beta.alpha':{}, 'beta.beta':{}}, 'charlie': {'charlie.alpha':{}, 'charlie.beta':{}, 'charlie.charlie':{}}, 'delta':{}}}


print_tree(tree)

output:

├── alpha
├── beta
│   ├── beta.alpha
│   └── beta.beta
├── charlie
│   ├── charlie.alpha
│   ├── charlie.beta
│   └── charlie.charlie
└── delta

Awesome little thing, looks much like the Linux tree utility, which I have long wanted to filter somehow. Probably will re-use your code, once I've understood how it works.

Hmmm, on second reading, that does look a bit like your special-casing first call. Oh well...

  • Related