Python-从嵌套字典中删除元素

时间:2018-09-12 03:27:50

标签: python dictionary

我正在使用下面的代码片段来比较两个对象,而忽略了一些键。但是,当我尝试从嵌套字典中删除字段时,该代码不起作用。 我需要忽略A中的lastElementC。以下代码能够完成忽略A中的lastElement而不是C的作用。

代码

def equal_dicts(d1, d2, ignore_keys=()):
    d1_, d2_ = d1.copy(), d2.copy()
    for k in ignore_keys:
        try:
            del d1_[k]
        except KeyError:
            pass
        try:
            del d2_[k]
        except KeyError:
            pass
    return json.dumps(d1_, sort_keys=True) == json.dumps(d2_, sort_keys=True)

尝试以上述方式执行

equal_dicts(data1, data2, ('A', 'C'['lastElement']))

预期输出:是。

数据1

{
    "A": "123456789",
    "B": {
        "firstElement": "abc",
        "lastElement": "def"
    },
    "C": {
        "firstElement": "chi",
        "lastElement": "jul"
    }
}

数据2

{
    "C": {
        "firstElement": "chi",
        "lastElement": "jul"
    },
    "A": "78901234",
    "B": {
        "firstElement": "abc",
        "lastElement": "e"
    }
}

3 个答案:

答案 0 :(得分:0)

您可以使用递归从嵌套词典中删除键。下面是一个有效的示例。

import json


def update_dict(original_dict, keys):
    return_dict = original_dict.copy()

    for k in keys:
        if isinstance(k, dict):
            _key = list(k.keys())[0]
            return_dict[_key] = update_dict(original_dict[_key], k.values())
        else:
            try:
                del return_dict[k]
            except KeyError:
                pass
    return return_dict


def equal_dicts(d1, d2, ignore_keys=()):
    d1_, d2_ = update_dict(d1, ignore_keys), update_dict(d2, ignore_keys)
    return json.dumps(d1_, sort_keys=True) == json.dumps(d2_, sort_keys=True)


data1 = {
    "A": "123456789",
    "B": {
        "firstElement": "abc",
        "lastElement": "def"
    },
    "C": {
        "firstElement": "chi",
        "lastElement": "abc"
    }
}

data2 = {
    "A": "78901234",
    "B": {
        "firstElement": "abc",
        "lastElement": "def"
    },
    "C": {
        "firstElement": "chi",
        "lastElement": "jul"
    }
}

print(equal_dicts(data1, data2, ('A', 'dateTime', 'trxId', {'C':'lastElement'})))

我不确定这是否满足您的要求,但是您现在可以传递字典。只会从预期的父级中删除该词典中的键。

答案 1 :(得分:0)

问题的很大一部分是您对嵌套键的规范不正确。您显然无法用另一个字符串索引一个字符串,因此'C'['lastElement']除了错误外不会给您任何其他信息。让我们使用元组或其他可迭代的方法来持有嵌套键:

equal_dicts(data1, data2, ('A', ('C', 'lastElement')))

现在清理字典应该很容易:

def remove_key(d, k):
    if not isinstance(d, dict): return
    try:
        if isinstance(k, str) or len(k) == 1:
            if not isinstance(k, str): k = k[0]
            del d[k]
        else:
            remove_key(d[k[0]], k[1:])
    except KeyError:
        pass

只需使用此功能代替del

请记住,您制作的副本很浅:删除嵌套键实际上也会将它们从原始对象中删除。您可以通过更新remove_key函数以仅在删除键时根据需要返回更新的字典来解决该问题。这不会比最有可能进行深度复制便宜很多,但是它应该更容易阅读:

def remove_key(d, key):
    if not isinstance(d, dict):
        return d
    if not isinstance(key, str):
        key, *rem = key
    else:
        rem = []
    if key not in d:
        return d
    if not rem:
        return {k: v for k, v in d.items() if k != key}
    e = remove_key(d[key], rem)
    if e is not d[key]:
         return {k: e if k == key else v for k, v in d.items()}
    return d

使用此版本分配副本:

for key in ignore_keys:
    d1 = remove_key(d1, key)
    d2 = remove_key(d2, key)

如果未删除任何密钥,则它们将保留为原始引用。任何删除的键都只会触发嵌套词典的必要级别的副本,尽管对于给定级别可能会多次发生。

对于最终的返回值,只需使用return d1 == d2。字典比较是通过实际的键和值完成的,而无需考虑排序。

答案 2 :(得分:0)

删除键的功能似乎不正确。除此之外,我还尝试对您的代码进行最小的更改。

from functools import reduce
import json

def delete_nested(dictionary, paths):
    """
    Delete the keys specified as path in paths from dictionary.
    """
    for path in paths:
        parent_path, last_key = path[:-1], path[-1]
        parent = reduce(dict.get, parent_path, dictionary)
        if(parent==None):
            sys.exit("The path {path} is invalid".format(path=path))
        if(not(isinstance(parent, dict))):
            sys.exit("The path {path} doesn't contain a dict".format(path=parent_path))

        del parent[last_key]

def equal(d1, d2, ignore_keys=[]):
    """
    Check if d1 and d2 are equal less the ignore_keys
    """
    d1_, d2_ = d1.copy(), d2.copy()
    delete_nested(d1_, ignore_keys)
    delete_nested(d2_, ignore_keys)
    return d1_ == d2_

执行示例:

d1 = json.loads("""{
    "A": "123456789",
    "B": {
        "firstElement": "abc",
        "lastElement": "def"
    },
    "C": {
        "firstElement": "chi",
        "lastElement": "jul"
    }
}""")

d2 = json.loads("""{
    "C": {
        "firstElement": "chi",
        "lastElement": "jul"
    },
    "A": "78901234",
    "B": {
        "firstElement": "abc",
        "lastElement": "e"
    }
}""")

print(equal(d1, d2, ["A",["B","lastElement"]])) # prints True