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

Python-从嵌套字典中删除元素,python,dictionary,Python,Dictionary,我正在使用下面的代码来比较两个对象,同时忽略几个键。但是,当我试图从嵌套字典中删除字段时,代码不起作用。 我需要从C中忽略A和lastElement。下面的代码能够从C中忽略A,但不能忽略lastElement 代码: def equal_dicts(d1, d2, ignore_keys=()): d1_, d2_ = d1.copy(), d2.copy() for k in ignore_keys: try: del d1_[k]

我正在使用下面的代码来比较两个对象,同时忽略几个键。但是,当我试图从嵌套字典中删除字段时,代码不起作用。 我需要从
C
中忽略
A
lastElement
。下面的代码能够从
C
中忽略
A
,但不能忽略
lastElement

代码

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)
{
    "A": "123456789",
    "B": {
        "firstElement": "abc",
        "lastElement": "def"
    },
    "C": {
        "firstElement": "chi",
        "lastElement": "jul"
    }
}
{
    "C": {
        "firstElement": "chi",
        "lastElement": "jul"
    },
    "A": "78901234",
    "B": {
        "firstElement": "abc",
        "lastElement": "e"
    }
}
尝试执行上述操作,如下所示:

equal_dicts(data1, data2, ('A', 'C'['lastElement']))
预期输出:真

数据1

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)
{
    "A": "123456789",
    "B": {
        "firstElement": "abc",
        "lastElement": "def"
    },
    "C": {
        "firstElement": "chi",
        "lastElement": "jul"
    }
}
{
    "C": {
        "firstElement": "chi",
        "lastElement": "jul"
    },
    "A": "78901234",
    "B": {
        "firstElement": "abc",
        "lastElement": "e"
    }
}
数据2

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)
{
    "A": "123456789",
    "B": {
        "firstElement": "abc",
        "lastElement": "def"
    },
    "C": {
        "firstElement": "chi",
        "lastElement": "jul"
    }
}
{
    "C": {
        "firstElement": "chi",
        "lastElement": "jul"
    },
    "A": "78901234",
    "B": {
        "firstElement": "abc",
        "lastElement": "e"
    }
}

可以使用递归从嵌套字典中删除键。下面是一个工作示例

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

我不确定这是否符合你的要求,但你现在可以通过字典。它只会从预期的父项中删除该字典中的键。

问题的一大部分是您没有适当的嵌套键规范。显然,您不能用另一个字符串索引一个字符串,因此
'C'['lastElement']
只会给您一个错误。让我们使用元组或其他iterable来保存嵌套键:

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)
如果没有删除任何键,这些键将保持原始引用的状态。任何删除的键都将只触发嵌套字典的必要级别的副本,尽管对于给定级别,这种情况可能会发生多次


对于最终返回值,只需使用
returnd1==d2
。字典比较是按实际键和值进行的,不考虑排序。

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

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

为什么不直接比较这两个dict,用
d1.==d2.
?为什么要比较json字符串表示形式?此外,代码中的dict没有
dateTime
trxId
键。这两个dict可能没有排序顺序。我想比较两个不包括“A”和“lastElement”的dict,因为CDict比较不要求它们按顺序排序。字典通常不按顺序排序。比较它们的键和值是否相等。这是不安全的,因为另一个子字典中可能有“lastElement”。我更新了代码,允许在字典中传递键。现在,它应该只正确地处理从正确的子字典中删除元素。