построение древовидного представления из вложенного списка с дублированными поддеревьями (используя anytree / treelib) - PullRequest
0 голосов
/ 17 июня 2019

У меня есть вложенный список, подобный следующему:

lst = [['a', 'b', 'e'],      # this e is the branch of b
       ['a', 'f', 'e'],      # this e is the branch of f,
       ['a', 'h', 'i i i']]  # string with spaces

и я хочу построить дерево вроде:

a
├── b
│   └── e
├── f
|   └── e
└── h
    └── i i i

Я хочу использовать любой из двух пакетов: treelib и anytree . Я прочитал много постов и перепробовал много разных методов, но это не помогло.

Обновление:

Я придумал следующий метод, но сейчас у меня проблемы

  1. вертикальный порядок ветвей (например, "b", "f", "h") не гарантируется (когда у меня много списков в списке).
  2. "e" как ветвь "f" не будет отображаться
from treelib import Node, Tree

# make list flat
lst = sum([i for i in lst], [])

tree = Tree()
tree_dict = {}

# create root node
tree_dict[lst[0]] = tree.create_node(lst[0])

for index, item in enumerate(lst[1:], start=1):
    if item not in tree_dict.keys():
        partent_node = tree_dict[lst[index-1]]
        tree_dict[item] = tree.create_node(item, parent=partent_node)

tree.show()

1 Ответ

1 голос
/ 18 июня 2019

Я посмотрел на anytree и придумал это:

from anytree import Node, RenderTree

lst = [["a", "b", "c", "e"], ["a", "b", "f"], ["a", "b", "c", "g", "h"], ["a", "i"]]


def list_to_anytree(lst):
    root_name = lst[0][0]
    root_node = Node(root_name)
    nodes = {root_name: root_node}  # keeping a dict of the nodes
    for branch in lst:
        assert branch[0] == root_name
        for parent_name, node_name in zip(branch, branch[1:]):
            node = nodes.setdefault(node_name, Node(node_name))
            parent_node = nodes[parent_name]
            if node.parent is not None:
                assert node.parent.name == parent_name
            else:
                node.parent = parent_node
    return root_node


anytree = list_to_anytree(lst)
for pre, fill, node in RenderTree(anytree):
    print(f"{pre}{node.name}")

здесь мало что происходит.я просто конвертирую ваш список в узлы anytree (и при этом assert представление списка действительно).и я храню словарь узлов, которые у меня уже есть в nodes.

, результат действительно

a
├── b
│   ├── c
│   │   ├── e
│   │   └── g
│   │       └── h
│   └── f
└── i

, если у вас есть несколько узлов с одинаковым именем, вы не можетеиспользуйте dict выше;вам нужно выполнить итерации от корневого узла по дочерним элементам:

def list_to_anytree(lst):
    root_name = lst[0][0]
    root_node = Node(root_name)
    for branch in lst:
        parent_node = root_node
        assert branch[0] == parent_node.name
        for cur_node_name in branch[1:]:
            cur_node = next(
                (node for node in parent_node.children if node.name == cur_node_name),
                None,
            )
            if cur_node is None:
                cur_node = Node(cur_node_name, parent=parent_node)
            parent_node = cur_node
    return root_node

ваш пример

lst = [
    ["a", "b", "e"],  # this e is the branch of b
    ["a", "f", "e"],  # this e is the branch of f,
    ["a", "h", "i i i"],
]

anytree = list_to_anytree(lst)
for pre, fill, node in RenderTree(anytree):
    print(f"{pre}{node.name}")

, затем даст:

a
├── b
│   └── e
├── f
│   └── e
└── h
    └── i i i
...