【问题标题】:how to recursively group dictionary by string in keys如何通过键中的字符串递归地对字典进行分组
【发布时间】:2020-04-24 18:09:08
【问题描述】:

我有一本像这样的字典:

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'}

我想按'__' 拆分键并将它们分组,然后按'__' 拆分这些键并将它们分组等,直到无法再分组。

最后,我会得到这样的结果:

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

在每个级别的分组中,我似乎都得到了想要的结果。当它到达不再发生分组的最终级别时,它返回一个无。我觉得我只是错过了最后几个步骤来将它们拼凑在一起。

请帮助编码专家!!

【问题讨论】:

  • 您确定不想要'simulation': {'config': {'name': .. 吗?如有必要,您始终可以再次将全名与__ 串在一起。 (我为此输出提供了一个不错的简短解决方案。)
  • 在这一点上,我确实需要字符串,但如果你有一个潜在的方法但不这样做,我很高兴看到它。如果需要,我可以在之后重新排列它们是(也许我可以修改我的其他代码以接受它)

标签: python recursion group-by itertools


【解决方案1】:

您可以通过首先创建一个包含所有键的链接字典,然后在一个循环中将每个子项连接到其父项来构建字典而无需递归:

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】:

如果您首先测试一个键是否已经在目标字典中,则不需要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'))

【讨论】:

  • 感谢您的帮助,您的解决方案确实有效,我认为我距离完成我的方法仅一步之遥,这给了我想要的结果 - 我给了你一个赞成票,但我想我会接受我的(当我可以时)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-31
  • 1970-01-01
  • 2015-05-18
  • 2015-10-27
  • 2015-08-05
  • 2017-03-27
  • 2017-04-02
相关资源
最近更新 更多