как рекурсивно сгруппировать словарь по строке в ключах - PullRequest
1 голос
/ 24 апреля 2020

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

d = {
'simulation__config__name': '0',
'simulation__config__ver': '1',
'simulation__config__rev': '2',
'simulation__model__name': '3',
'simulation__name': '4',
'site__name': '5',
'site__placement': '6',
'name': '7'}

Я хочу разделить ключи по '__' и сгруппировать их, затем разделить эти ключи по '__' и сгруппировать их, et c., пока больше нет возможности группировки.

В конце я получу такой результат:

out = {
 'simulation': {'simulation__config': {'simulation__config_name': 0,
                                       'simulation__config_ver': 1,
                                       'simulation__config_rev': 2},
                'simulation__model': {'simulation__model_name': 3},
                'simulation__name': 4},
 'site': {'site__name': 5, 'site__placement': 6},
 'name': 7}

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

Это мой текущий код:

from itertools import groupby

d = {
'simulation__config__name': '0',
'simulation__config__ver': '1',
'simulation__config__rev': '2',
'simulation__model__name': '3',
'simulation__name': '4',
'site__name': '5',
'site__placement': '6',
'name': '7'}

def get_key(string, i):
    return '__'.join(string.split('__')[0:i+1])

def recursive_group(iterable, i):
    if not isinstance(iterable, dict):
        return
    out = {}
    for k,g in groupby(iterable.keys(), lambda x: get_key(x,i)):
        inner = {key:iterable[key] for key in g}
        if k in iterable.keys():
            out.update(inner)
        else:
            out.update({k:inner})
    return out

out = recursive_group(d, 0)
#{'simulation': {'simulation__config__name': '0', 'simulation__config__ver': '1', 'simulation__config__rev': '2', 'simulation__model__name': '3', 'simulation__name': '4'}, 'site': {'site__name': '5', 'site__placement': '6'}, 'name': '7'}

out1 = recursive_group(out['simulation'], 1)
#{'simulation__config': {'simulation__config__name': '0', 'simulation__config__ver': '1', 'simulation__config__rev': '2'}, 'simulation__model': {'simulation__model__name': '3'}, 'simulation__name': '4'}

out2 = recursive_group(out1['simulation__config'], 2)
#{'simulation__config__name': '0', 'simulation__config__ver': '1', 'simulation__config__rev': '2'}

out3 = recursive_group(out2['simulation__config__name'], 3)
#None

out4 = recursive_group(out['site'], 1)
#{'site__name': '5', 'site__placement': '6'}

out5 = recursive_group(out4['site__name'], 2)
#None

out6 = recursive_group(out['name'], 1)
#None

вниз на каждом уровне группировки, я, кажется, получить желаемый результат. и когда он достигает конечного уровня, на котором больше не может происходить группировка, он возвращает None. Мне кажется, я просто пропускаю пару последних шагов, чтобы собрать все воедино.

Пожалуйста, помогите экспертам по кодированию !!

Ответы [ 2 ]

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

Вы можете создать словарь без рекурсии, сначала создав словарь ссылок со всеми ключами, а затем подключив каждого потомка к его родителю в одном выводе l oop:

d = {
'simulation__config__name': '0',
'simulation__config__ver': '1',
'simulation__config__rev': '2',
'simulation__model__name': '3',
'simulation__name': '4',
'site__name': '5',
'site__placement': '6',
'name': '7'}

from itertools import accumulate
keys  = {"__".join(pk) for k in d for pk in accumulate([s] for s in k.split("__"))}
links = { key:{} for key in keys }
out   = links[""] = dict()
for key in keys:
    parentKey = "__".join(key.split("__")[:-1])
    links[parentKey].update({key:d.get(key,links[key])}) # value from d for leaf nodes

:

print(out)

{'site': {'site__placement': '6', 'site__name': '5'},
 'name': '7',
 'simulation':
     {'simulation__model':
         {'simulation__model__name': '3'},
      'simulation__name': '4',
      'simulation__config':
         {'simulation__config__ver': '1',
          'simulation__config__rev': '2',
          'simulation__config__name': '0'
         }
     }
}

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

for key,value in links.items():
    if isinstance(value,dict):
        for sk in list(value):
            value[sk.split("__")[-1]] = value.pop(sk)

print(out)

{'name': '7',
 'simulation':
     {'config': {'ver': '1', 'rev': '2', 'name': '0'},
      'name': '4',
      'model': {'name': '3'}
     },
 'site': {'placement': '6', 'name': '5'}
 }
2 голосов
/ 24 апреля 2020

Вам не нужно groupby, если вы сначала проверяете, есть ли уже ключ в поле назначения. Если это не так, создайте, иначе, добавьте.

Следующий код

import pprint

d = {
'simulation__config__name': '0',
'simulation__config__ver': '1',
'simulation__config__rev': '2',
'simulation__model__name': '3',
'simulation__name': '4',
'site__name': '5',
'site__placement': '6',
'name': '7'}

def recursive_group (iterable):
    out = {}
    for key in iterable:
        if '__' in key:
            left,right = key.split('__',1)
            if left not in out:
                out[left] = {}
            out[left][right] = iterable[key]
            if '__' in right:
                out[left] = recursive_group(out[left])
        else:
            out[key] = iterable[key]

    return out


out = recursive_group (d)
pprint.pprint (out, sort_dicts=False)

приводит к аккуратному, компактному вложенному словарю:

{'simulation': {'config': {'name': '0', 'ver': '1', 'rev': '2'},
                'model': {'name': '3'},
                'name': '4'},
 'site': {'name': '5', 'placement': '6'},
 'name': '7'}

Вы можете получить значения в этом результате с исходной полной строковой нотацией с использованием другой рекурсивной функции:

def get_value (iterable, full_name):
    if '__' in full_name:
        left,right = full_name.split('__',1)
        return get_value(iterable[left], right)
    return iterable[full_name]

print (get_value (out, 'simulation__config__ver'))
...