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

How to flatten a nested dict and use inner values on collision

问题

对于以下词典:

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

受我在这里读到的内容的启发,主字典的扁平化函数,无论你的键字典有多深:

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}

例如更深的迪科:

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}

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

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

你可以试试

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}

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

扁平化字典本身就是一件完整的事情。这是否实现了您的目标? (未经测试,我正在输入 phone a.t.m。):

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"])

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

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