Можно ли сгладить глубоко вложенный словарь Python, который содержит значения со списками более вложенных словарей? - PullRequest
2 голосов
/ 15 апреля 2020

Я работаю с большим файлом xml, в котором я пытался извлечь ключи и значения. Информация в этом файле очень конфиденциальна, поэтому я не могу поделиться ею. Я начал с использования библиотеки xml. Однако после нескольких часов разочарования я обнаружил библиотеку xmltodict. Я использовал эту библиотеку, чтобы преобразовать мой xml в словарь (что-то, с чем я гораздо лучше знаком относительно xml).

import xmltodict

# convert xml to dictionary
dict_nested = xmltodict.parse(str_xml)

Теперь, когда xml - это словарь, я бы хотел чтобы сгладить это, потому что есть большое количество уровней (я не знаю, сколько уровней), при создании имен ключей, которые помогают мне проследить путь к их соответствующему значению. Таким образом, я попытался:

from flatten_dict import flatten

# flatten dict_nested 
dict_flat = flatten(dict_nested)

Результат может выглядеть примерно так, но со многими другими слоями:

{'ID': '123',
 'info': [{'breed':'collie'}, 
          {'fur': [{'short':'no'}, 
                   {'color':[{'black':'no'},
                             {'brown':'yes'}]}]}]}

Это сработало, так как мои ключи - это кортежи, показывающие путь слоев. Моими значениями являются либо строки (то есть конечный результат, который я ищу), либо списки типа OrderedDict .

Так как каждый словарь в каждом списке должен быть сведен, и я не знаю, насколько глубоко это идет, я пытаюсь найти способ программно выравнивать все словари, пока все ключи не соответствуют одному значению (то есть, не список или словарь).

В идеале выходные данные должны выглядеть примерно так:

{'ID':'123',
 'info_breed':'collie',
 'info_fur_short':'no',
 'info_fur_color_black':'no',
 'info_fur_color_brown':'yes'}

Извините, что не могу поделиться большей частью своих результатов из-за конфиденциальной информации.

Ответы [ 2 ]

2 голосов
/ 15 апреля 2020

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

dict_flat = {'ID': '123',
 'info': [{'breed':'collie'}, 
          {'fur': [{'short':'no'}, 
                   {'color':[{'black':'no'},
                             {'brown':'yes'}]}]}]}

def my_flatten(dict_flat, key_prefix=None):

    result = {}
    for k, v in dict_flat.items():
        key = f'{key_prefix}_{k}' if key_prefix is not None else k
        if isinstance(v, list):
            for d in v:
                result.update(my_flatten(d, key))
        else:
            result[key] = v
    return result

my_flatten(dict_flat)

output:

{'ID': '123',
 'info_breed': 'collie',
 'info_fur_short': 'no',
 'info_fur_color_black': 'no',
 'info_fur_color_brown': 'yes'}
1 голос
/ 15 апреля 2020

Другой подход заключается в создании генератора, который выдает кортежи ключ / значение. Вы можете просто передать это в конструктор словаря:

d = {'ID': '123',
     'info': [{'breed':'collie'}, 
          {'fur': [{'short':'no'}, 
               {'color':[{'black':'no'},
                         {'brown':'yes'}]}]}]}

def flatten(obj, prefix=[]):
    if isinstance(obj, str):
        yield ('_'.join(prefix), obj)

    elif isinstance(obj, list):
        for o in obj:
            yield from flatten(o, prefix) 
    else:
        for k, v in obj.items():
            yield from flatten(v, prefix + [k])


dict(flatten(d))

Результат:

{'ID': '123',
 'info_breed': 'collie',
 'info_fur_short': 'no',
 'info_fur_color_black': 'no',
 'info_fur_color_brown': 'yes'}

Это позволяет избежать управления словарем в функциях, которые, в зависимости от того, что вам нравится в вещах, могут проще рассуждать.

...