Given input:
apple: banana eggplant
banana: cantaloupe durian
eggplant:
fig:
I would like to concatenate it into the format:
├─ apple
│ ├─ banana
│ │ ├─ cantaloupe
│ │ └─ durian
│ └─ eggplant
└─ fig
There may or may not be multiple root elements (in the above example, there are two root elements), and I would like to find a solution that handles them without an issue.
Are there any command line tools that can handle this kind of transformation? Failing that, is there anything in other scripting languages that can handle this somewhat easily (I've looked at Python's pprint
but I'm not sure how to use it for something like this either)?
following code shall produce the tree structure you are asking for:
branch = '├'
pipe = '|'
end = '└'
dash = '─'class Tree(object):def __init__(self, tag):self.tag = tagclass Node(Tree):def __init__(self, tag, *nodes):super(Node, self).__init__(tag)self.nodes = list(nodes)class Leaf(Tree):passdef _draw_tree(tree, level, last=False, sup=[]):def update(left, i):if i < len(left):left[i] = ' 'return leftprint ''.join(reduce(update, sup, ['{} '.format(pipe)] * level)) \+ (end if last else branch) + '{} '.format(dash) \+ str(tree.tag)if isinstance(tree, Node):level += 1for node in tree.nodes[:-1]:_draw_tree(node, level, sup=sup)_draw_tree(tree.nodes[-1], level, True, [level] + sup)def draw_tree(trees):for tree in trees[:-1]:_draw_tree(tree, 0)_draw_tree(trees[-1], 0, True, [0])
it requires you represent the data using given form.
about your data deserialization, you just need to keep track of the parent nodes, such that when a leaf appears to be a node, you just replace it:
class Track(object):def __init__(self, parent, idx):self.parent, self.idx = parent, idxdef parser(text):trees = []tracks = {}for line in text.splitlines():line = line.strip()key, value = map(lambda s: s.strip(), line.split(':', 1))nodes = value.split()if len(nodes):parent = Node(key)for i, node in enumerate(nodes):tracks[node] = Track(parent, i)parent.nodes.append(Leaf(node))curnode = parentif curnode.tag in tracks:t = tracks[curnode.tag]t.parent.nodes[t.idx] = curnodeelse:trees.append(curnode)else:curnode = Leaf(key)if curnode.tag in tracks:# well, how you want to handle it?pass # ignoreelse:trees.append(curnode)return trees
it runs:
>>> text='''apple: banana eggplant
banana: cantaloupe durian
eggplant:
fig:'''
>>> draw_tree(parser(text))
├─ apple
| ├─ banana
| | ├─ cantaloupe
| | └─ durian
| └─ eggplant
└─ fig
hope it fully deals with your problem.
update
my code offers some concern over corner cases, for example:
>>> text='''apple: banana eggplant
banana: cantaloupe durian
eggplant:'''
>>> draw_tree(parser(text))
└─ apple├─ banana| ├─ cantaloupe| └─ durian└─ eggplant
notice the left most side of subnodes of apple
, there is no |
to the end because they are suppressed.
or empty in the middle:
>>> text='''apple: banana
banana: cantaloupe durian
eggplant:'''
>>> draw_tree(parser(text))
├─ apple
| └─ banana
| ├─ cantaloupe
| └─ durian
└─ eggplant