如何展平嵌套的字典并在碰撞时使用内部值

Posted

技术标签:

【中文标题】如何展平嵌套的字典并在碰撞时使用内部值【英文标题】:How to flatten a nested dict and use inner values on collision 【发布时间】:2020-09-18 04:09:48 【问题描述】:

问题

对于以下字典:

'id': 1, 'label': 'hello', 'remove_me': 'world': 'keep_me': 52

我想创建一个没有 remove_meworld 键的新字典。

编辑更新:

总而言之,我想做以下事情:

如果一个项目的值是一个嵌套字典。使用内部结果更新新字典,同时从主字典中删除当前键值。

如果项目的值不是嵌套字典,则使用键值更新新字典。

接受的答案涵盖了这一点。

我尝试了什么?

k:v for (k,v) in d.items() if not k == 'remove_me'

产量:

'id': 1, 'label': 'hello'

不完全是我需要的,因为我正在删除嵌套的字典。

期望的输出:

'id': 1, 'label': 'hello','keep_me': 52

【问题讨论】:

还有其他类似的键可以应用吗?如果它只是有问题的特定键,那么手动分配该键值对似乎会更容易 如果“remove_me”有多个键,或者有一个不是“世界”的键,你想做什么? 【参考方案1】:
dico = 'id': 1, 'label': 'hello', 'remove_me': 'world': 'keep_me': 52

# Just what you have done
new_dico = k:v for (k,v) in dico.items() if not k == 'remove_me'

# Plus this line
new_dico.update(dico['remove_me']['world'])

print(new_dico)
# 'id': 1, 'label': 'hello', 'keep_me': 52

受我在此处阅读的内容的启发,主 dict 的扁平化函数,无论您的 key-dict 是什么深度:

dico = 'id': 1, 'label': 'hello', 'remove_me': 'world': 'keep_me': 52

def dFlatten(dico, d = ):
    for k, v in dico.items():
        if isinstance(v, dict):
            dFlatten(v)
        else:
            d[k] = v
    return d
dico = dFlatten(dico)
print(dico)
# 'id': 1, 'label': 'hello', 'keep_me': 52

例如更深的 dico:

dico2 = 'id': 1, 'label': 'hello', 'stuff1': 'stuff2': 'remove_me': 'world': 'keep_me': 52
dico2 = dFlatten(dico2)
print(dico2)         
# 'id': 1, 'label': 'hello', 'keep_me': 52

具有相同 dFlatten 功能的多个深度键

dico3 = 'id': 1, 'label': 'hello', 'deep': 'L1': 'L2': 52, 'remove_me': 'world': 'keep_me': 52
dico3 = dFlatten(dico3)
print(dico3)         
# 'id': 1, 'label': 'hello', 'keep_me': 52, 'L2': 52

【讨论】:

【参考方案2】:

你可以试试

d = 'id': 1, 'label': 'hello', 'remove_me': 'world': 'keep_me': 52
for k, v in list(d.items()):
    if isinstance(v, dict):
        for i in v:
           if isinstance(v[i], dict):
               d.update(v[i])
           else:
               d.update(v)
        del d[k]
print(d)

输出

'id': 1, 'label': 'hello', 'keep_me': 52

此代码将检查每个项目的值是否为 dict,如果是,它将使用内部结果更新 dict 并从主 dict 中删除当前键值。 这样,最后,d dict 将只保留一个键和一个字符串值,而没有嵌套的 dicts。

【讨论】:

这只是删除嵌套的字典不是特定的键。这会因d = 'id': 1, 'label': 'hello', 'remove_me': 'world': 'keep_me': 52, 'test':2 之类的字典而失败它还会错误地删除带有d = 'id': 1, 'label': 'test':'hello' 之类的字典的好键 是的@MateenUlhaq,您只需将remove_me 更改为dont_remove_me 即可让灾难发生。 旁注,我不得不将for k, v in d.items(): 更改为for k, v in list(d.items()): 以避免RuntimeError: dictionary keys changed during iteration @awarrier99 感谢您根据您的说明更改了代码 @LeoArad 它仍然会删除好的键。例如:d = 'id': 1, 'label': 'hello', 'dont_remove_me': 'world': 'keep_me': 52 返回'id': 1, 'label': 'hello', 'keep_me': 52 dont_remove_me 发生了什么?【参考方案3】:

您可能需要更具体地了解字典的结构,以及如何处理多个键。无论如何,这是一种符合您的描述并尝试保留尽可能多的键的递归方法。

def clean_kvp(k, v, invalid_keys=["remove_me", "world"]):
    if k not in invalid_keys:
        return [(k, v)]
    if not isinstance(v, dict):
        return []
    return [
        (kkk, vvv)
        for kk, vv in v.items()
        for kkk, vvv in clean_kvp(kk, vv)
    ]

def clean_dict(d):
    return 
        kk: vv
        for k, v in d.items()
        for kk, vv in clean_kvp(k, v)
    

几个测试:

>>> d = 'id': 1, 'label': 'hello', 'remove_me': 'world': 'keep_me': 52
>>> clean_dict(d)
'id': 1, 'label': 'hello', 'keep_me': 52

>>> d = 
...     'id': 1,
...     'label': 'hello',
...     'remove_me': 'world': 'keep_me': 52, 'test': 2
... 
>>> clean_dict(d)
'id': 1, 'label': 'hello', 'keep_me': 52, 'test': 2

>>> d = 'id': 1, 'label': 'test': 'hello'
>>> clean_dict(d)
'id': 1, 'label': 'test': 'hello'

【讨论】:

【参考方案4】:

我认为这可以在一般情况下使用单个递归函数来完成,例如:

def remove_keys(d, keys):
    if not isinstance(d, dict):
        return d 
    ret = 
    for k, v in d.items():
        clean = remove_keys(v, keys)
        if k not in bad_keys:
            ret[k] = clean
        else:
            if isinstance(clean, dict):
                ret.update(clean)

    return ret

bad_keys = ['remove_me', 'world']

d = 'id': 1, 'label': 'hello', 'remove_me': 'world': 'keep_me': 52
remove_keys(d, bad_keys)
# 'id': 1, 'label': 'hello', 'keep_me': 52

d = 'id': 1, 'label': 'hello', 'remove_me': 52
remove_keys(d, bad_keys)
# 'id': 1, 'label': 'hello'

d = 'id': 1, 'label': 'hello', 'dont_remove_me': 'world': 'keep_me': 52
remove_keys(d, bad_keys)
# 'id': 1, 'label': 'hello', 'dont_remove_me': 'keep_me': 52

【讨论】:

【参考方案5】:

扁平化字典本身就是一回事。这是否实现了您的目标? (未经测试,我上午在手机上打字):

def flattenDict(myDict, blacklist):
  returnDict = 
  for key, val in myDict:
    If isinstance(val, "dict"):
       myDict.update(flattenDict(val))
    elif key in blacklist:
       continue 
    elif val in blacklist:
        continue
    returnDict[key] = val
  return returnDict 

cleanDict = flattenDict(myDict, ["remove_me", "world"])

【讨论】:

以上是关于如何展平嵌套的字典并在碰撞时使用内部值的主要内容,如果未能解决你的问题,请参考以下文章

如何展平多级/嵌套 JSON?

pandas json_normalize 展平嵌套字典

来自数据框的嵌套字典,内部字典包含熊猫系列作为值

在 Python 中展平嵌套的 JSON API 字典

如何展平嵌套的结果?

具有数组和字典混合的横向展平雪管数据