2015-12-09 5 views
2

У меня есть это весьма вложенная словарь дерево:Свести словарь словарей списков других словарей списков

sample = {"name": "one", 
      "id": "1", 
      "children": [{"name": "two", 
         "id": "2", 
         "children": [{"name": "six", 
             "id": "6", 
             "children": []}, 
            {"name": "seven", 
             "id": "7", 
             "children": []}]}, 
         {"name": "three", 
         "id": "3", 
         "children": []}, 
         {"name": "four", 
         "id": "4", 
         "children": []}, 
         {"name": "five", 
         "id": "5", 
         "children": []}]} 

Это просто пример, в действительности есть 7 или 8 уровней списков детей .. Также каждое имя и идентификатор уникальны.

Моя цель состоит в том, чтобы сгладить это дерево в словарь, который имеет значение всех ключей имен в качестве ключей и их идентификаторы в качестве второго ключа пары значений:

sample = {"one": {"id":"1"}, 
      "two": {"id":"2"}, 
      "three": {"id": "3"}, ...} 

На самом деле есть несколько пар ключ-значение, но Меня интересуют только имена и связанные с ними идентификаторы.

Я попытался обмотать голову, но мои навыки в рекурсиях не очень хорошие, было трудное время, поэтому я прошу вас о помощи. Также я искал аналогичные проблемы, но тот факт, что dicts инкапсулирован в списки, делает его не очень сопоставимым, для меня так или иначе ...

Я придумал решение для решения моей проблемы, но это был самый хакерский и уродливый код, который я каждый писал, и мне стыдно за это. В основном я преобразовал dict в его строковое представление и использовал regex, чтобы найти мои пары! И это плохо, но мне пришлось прототипировать что-то, чтобы успеть позаботиться о других проблемах ...

Итак, любые идеи, ребята?

+2

Почему не просто '{ "один": "1" , "two": "2", ...} '? –

+0

@tobias_k это также возможный выход. Но я не могу изменить дерево ввода, это дано и вызывает у меня головные боли. –

+2

Обратите внимание, что у вас могут быть дубликаты ключей, которые переписывают данные, поэтому вам придется либо проверить это перед рукой, либо убедиться, что это не так с помощью исходного метода кодирования. Например, дети - это список словарей, поэтому нет ничего, чтобы ключи были уникальными. – Alexander

ответ

3

Вы можете сделать рекурсивную функцию следующим образом (при условии, что каждый из dicts правильно структурировать):

def flatten(source, target): 
    target[source["name"]] = {"id": source["id"]} 
    for child in source["children"]: 
     flatten(child, target) 

Пример:

>>> d = {} 
>>> flatten(sample, d) 
>>> d 
{'seven': {'id': '7'}, 'six': {'id': '6'}, 'three': {'id': '3'}, 'two': {'id': '2'}, 'four': {'id': '4'}, 'five': {'id': '5'}, 'one': {'id': '1'}} 

Или, как это, если вам не нравится прохождение мишень ДИКТ в качестве параметра:

def flatten(source): 
    d = {source["name"]: {"id": source["id"]}} 
    for child in source["children"]: 
     d.update(flatten(child)) 
    return d 

Пример:

>>> flatten(sample) 
{'one': {'id': '1'}, 'four': {'id': '4'}, 'seven': {'id': '7'}, 'five': {'id': '5'}, 'six': {'id': '6'}, 'three': {'id': '3'}, 'two': {'id': '2'}} 

Можно также упростить вывод, чтобы быть простой, не вложенная словарь:

def flatten(source): 
    d = {source["name"]: source["id"]} 
    for child in source["children"]: 
     d.update(flatten(child)) 
    return d 

>>> flatten(sample) 
{'one': '1', 'four': '4', 'seven': '7', 'five': '5', 'six': '6', 'three': '3', 'two': '2'} 
+0

Хм, я получил TypeError здесь: TypeError Traceback (самый последний вызов последнего) в () 6 новый = {} ----> 7 Flatten (пример, новый) 8 новый в flatten (источник, цель) 1 def flatten (источник, цель): ----> 2 target [source ["name"]] = { "ID": источник [ "ID"]} 3 для ребенка в источнике [ "дети"]: 4 Flatten (ребенок, мишень) TypeError: строковые индексы должны быть целыми –

+0

Python 3.4 ядра на IPython Нет tebook, это отслеживающий: TypeError Traceback (самый последний вызов последнего) в () 6 новый = {} ----> 7 Flatten (образец, новый) 8 новый в плоскости (источник, цель) 1 def flatten (источник, цель): ----> 2 target [source ["name"]] = {" идентификатор ": источник [" ID "]} 3 для ребенка в источнике [" дети "]: 4 Flatten (ребенок, мишень) TypeError: строковые индексы должны быть целыми –

+0

Похоже, что структура словаря отличается nt/более сложный, чем в вашем примере. В частности, в списке «дети», похоже, есть строки. Вы можете добавить 'if isinstance (source, dict)' здесь и там, но это трудно сказать, не видя вашу фактическую структуру словаря. –

2

Если ваши данные на самом деле более сложным, чем ваш пример:

def rec_get(d, k): 
    if isinstance(d, dict): 
     if k in d: 
      yield (d[k], {"id": d["id"]}) 
     for v in d.values(): 
      yield from rec_get(v, k) 
    elif isinstance(d, list): 
     for v in d: 
      yield from rec_get(v, k) 
print(dict(rec_get(sample ,"name"))) 

Выход:

{'five': {'id': '5'}, 'six': {'id': '6'}, 'four': {'id': '4'}, 'one': {'id': '1'}, 'three': {'id': '3'}, 'two': {'id': '2'}, 'seven': {'id': '7'}} 

Если вам нужна более общая функция, вы могли бы так повесят как:

from collections import OrderedDict 
from collections import Iterable 


def rec_get(d, **kwargs): 
    if isinstance(d, dict): 
     yield from ((d[k], k) for k in kwargs.keys() & d) 
     for v in d.values(): 
      yield from rec_get(v, **kwargs) 
    elif isinstance(d, Iterable) and not isinstance(d, str): 
     for v in d: 
      yield from rec_get(v, **kwargs) 

Который только в качестве примера, используя ввод и проходящие ключевые слова аргументы:

print(list(rec_get(sample, name="name", id="id"))) 

Выходы:

[('1', 'id'), ('one', 'name'), ('2', 'id'), ('two', 'name'), 
('6', 'id'), ('six', 'name'), ('7', 'id'), ('seven', 'name'), 
('3', 'id'), ('three', 'name'), ('4', 'id'), ('four', 'name'), ('5', 'id'), ('five', 'name')] 
+0

Это выглядит интересно, урожай от того, что у меня есть проблемы. Однако, интересно, с учетом времени, ваше решение почти в 10 раз медленнее, чем версия от @tobias_k. Неужели «isinstance» так дорого? –

+0

@KleinerNull, он будет работать намного больше, если он проверит весь dict, если вы добавите некоторые имена и id в список пустых детей, вы увидите, что я имею в виду. –

+0

Интересно, я буду тестировать его завтра с примером реальной жизни. Просто из соображений удобства я могу легко изменить это, чтобы сохранить другие диктофоны внутри, кроме списка детей? –

Смежные вопросы