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)?
You need to use command called tree. It will list contents of directories in a tree-like format. It is a recursive directory listing program that produces a depth indented listing of files. When directory arguments are given, tree lists all the files and/or directories found in the given directories each in turn.
It will display the contents of the working directory recursively showing sub-directories and files, and a summary of the total number of sub-directories and files. You can enable the printing of hidden files using the -a flag. 2.
The “tree” command is a very extensively used Bash command in Linux. It is used to display the contents of any desired directory of your computer system in the form of a tree structure.
Actually displaying trees with the tree command is simple. Simply calling tree in the current directory will show a tree of the directory. You can also have tree follow symbolic links on the system with the -l option. Otherwise, it'll display symbolic links with the "link -> target" format.
following code shall produce the tree structure you are asking for:
branch = '├'
pipe = '|'
end = '└'
dash = '─'
class Tree(object):
def __init__(self, tag):
self.tag = tag
class Node(Tree):
def __init__(self, tag, *nodes):
super(Node, self).__init__(tag)
self.nodes = list(nodes)
class Leaf(Tree):
pass
def _draw_tree(tree, level, last=False, sup=[]):
def update(left, i):
if i < len(left):
left[i] = ' '
return left
print ''.join(reduce(update, sup, ['{} '.format(pipe)] * level)) \
+ (end if last else branch) + '{} '.format(dash) \
+ str(tree.tag)
if isinstance(tree, Node):
level += 1
for 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, idx
def 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 = parent
if curnode.tag in tracks:
t = tracks[curnode.tag]
t.parent.nodes[t.idx] = curnode
else:
trees.append(curnode)
else:
curnode = Leaf(key)
if curnode.tag in tracks:
# well, how you want to handle it?
pass # ignore
else:
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.
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
This question is old, but here is a networkx version of the first solution:
def nx_ascii_tree(graph, key=None):
"""
Creates an printable ascii representation of a directed tree / forest.
Args:
graph (nx.DiGraph): each node has at most one parent (
i.e. graph must be a directed forest)
key (str): if specified, uses this node attribute as a label instead of
the id
References:
https://stackoverflow.com/questions/32151776/visualize-tree-in-bash-like-the-output-of-unix-tree
Example:
>>> import networkx as nx
>>> graph = nx.dfs_tree(nx.balanced_tree(2, 2), 0)
>>> text = nx_ascii_tree(graph)
>>> print(text)
└── 0
├── 1
│ ├── 3
│ └── 4
└── 2
├── 5
└── 6
"""
import six
import networkx as nx
branch = '├─'
pipe = '│'
end = '└─'
dash = '─'
assert nx.is_forest(graph)
assert nx.is_directed(graph)
lines = []
def _draw_tree_nx(graph, node, level, last=False, sup=[]):
def update(left, i):
if i < len(left):
left[i] = ' '
return left
initial = ['{} '.format(pipe)] * level
parts = six.moves.reduce(update, sup, initial)
prefix = ''.join(parts)
if key is None:
node_label = str(node)
else:
node_label = str(graph.nodes[node]['label'])
suffix = '{} '.format(dash) + node_label
if last:
line = prefix + end + suffix
else:
line = prefix + branch + suffix
lines.append(line)
children = list(graph.succ[node])
if children:
level += 1
for node in children[:-1]:
_draw_tree_nx(graph, node, level, sup=sup)
_draw_tree_nx(graph, children[-1], level, True, [level] + sup)
def draw_tree(graph):
source_nodes = [n for n in graph.nodes if graph.in_degree[n] == 0]
if source_nodes:
level = 0
for node in source_nodes[:-1]:
_draw_tree_nx(graph, node, level, last=False, sup=[])
_draw_tree_nx(graph, source_nodes[-1], level, last=True, sup=[0])
draw_tree(graph)
text = '\n'.join(lines)
return text
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With