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 theprefix
by making the defaultNone
- Added a third marker option which only root uses
- Replaced
children
list withenumerate
usingi
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...