#python #python-3.x #dictionary
#python #python-3.x #словарь
Вопрос:
У меня есть мои данные в этом формате —
data = {
"label": "xyz.com",
"children": [
{
"parent": "abc.com",
"label": "user-3",
"depth": 1
},
{
"parent": "xyz.com",
"label": "abc.com",
"depth": 0
},
{
"parent": "xyz.com",
"label": "user-1",
"depth": 0
}
]
}
Я хочу сгенерировать результат, который выглядит следующим образом (вложенный словарь) —
result = {
"label": "xyz.com",
"children": [
{
"parent": "xyz.com",
"label": "user-1",
"depth": 0
},
{
"parent": "xyz.com",
"label": "abc.com",
"depth": 0,
"children": [
{
"parent": "abc.com",
"label": "user-3",
"depth": 1
}
]
}
]
}
Исходный словарь преобразуется во вложенный словарь с использованием меток — parent и depth .
Кроме того, может быть любое количество дочерних элементов. Например —
result = {
"lable": "xyz.com",
"children": [
{
"parent": "xyz.com",
"label": "user-1",
"depth": 0
},
{
"parent": "xyz.com",
"label": "abc.com",
"depth": 0,
"children": [
{
"parent": "abc.com",
"label": "user-3",
"depth": 1,
"children": [
{...}, {...}
]
}
]
}
]
}
Не уверен, что рекурсия — это правильный путь или есть какое-то другое решение.
Спасибо за помощь.
Ответ №1:
Гораздо более эффективным и однопроходным линейным подходом является создание dict, который сопоставляет метки с узлами, чтобы вы могли легко получить родительский узел, просто просмотрев родительскую метку в dict и добавив узел к children
вложенному списку родительского узла. Используется dict.setdefault
для инициализации записи dict для родительского узла сначала, если итерация поступает на дочерний узел раньше, чем его родительский узел, чтобы родительский узел мог позже обновить себя с помощью уже существующей children
записи:
result = {'label': data['label']}
nodes = {data['label']: result}
for node in data['children']:
node.update(nodes.get(node['label'], {}))
nodes[node['label']] = node
nodes.setdefault(node['parent'], {}).setdefault('children', []).append(node)
result
становится:
{'label': 'xyz.com',
'children': [{'parent': 'xyz.com',
'label': 'abc.com',
'depth': 0,
'children': [{'parent': 'abc.com',
'label': 'user-3',
'depth': 1}]},
{'parent': 'xyz.com', 'label': 'user-1', 'depth': 0}]}
Ответ №2:
Вы можете использовать рекурсию:
data = {'label': 'xyz.com', 'children': [{'parent': 'abc.com', 'label': 'user-3', 'depth': 1}, {'parent': 'xyz.com', 'label': 'abc.com', 'depth': 0}, {'parent': 'xyz.com', 'label': 'user-1', 'depth': 0}]}
def group(d):
new_d = [i for i in d if all(c.get('label') != i.get('parent') for c in d)]
c = [(lambda x:{a:b for a, b in x.items() if a != 'children' or b})({**i, 'children':[*i.get('children', []), *[j for j in d if j.get('parent') == i['label']]]}) for i in new_d]
return [i if 'children' not in i else {**i, 'children':group(i['children'])} for i in c]
import json
print(json.dumps(group([data]), indent=4))
Вывод:
[
{
"label": "xyz.com",
"children": [
{
"parent": "xyz.com",
"label": "abc.com",
"depth": 0,
"children": [
{
"parent": "abc.com",
"label": "user-3",
"depth": 1
}
]
},
{
"parent": "xyz.com",
"label": "user-1",
"depth": 0
}
]
}
]
Ответ №3:
Более простой реализацией рекурсивного подхода было бы воспользоваться преимуществами ключей, уже имеющихся в dicts, и изменять dicts вместо создания новых:
def convert(nodes, parent):
children = [convert(nodes, node) for node in nodes if node['parent'] == parent['label']]
if children:
parent['children'] = children
return parent
итак, это convert(data.pop('children'), data)
возвращает:
{'label': 'xyz.com',
'children': [{'parent': 'xyz.com',
'label': 'abc.com',
'depth': 0,
'children': [{'parent': 'abc.com',
'label': 'user-3',
'depth': 1}]},
{'parent': 'xyz.com', 'label': 'user-1', 'depth': 0}]}