比较 Python 字典和嵌套字典

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27265939/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 01:35:37  来源:igfitidea点击:

Comparing Python dictionaries and nested dictionaries

pythondictionarycomparison

提问by rkatkam

I know there are several similar questions out there, but my question is quite different and difficult for me. I have two dictionaries:

我知道那里有几个类似的问题,但我的问题对我来说完全不同且困难。我有两本词典:

d1 = {'a': {'b': {'cs': 10}, 'd': {'cs': 20}}}
d2 = {'a': {'b': {'cs': 30}, 'd': {'cs': 20}}, 'newa': {'q': {'cs': 50}}}

i.e. d1has key 'a', and d2has keys 'a'and 'newa'(in other words d1is my old dict and d2is my new dict).

d1有键'a'd2有键'a''newa'(换句话说,d1是我的旧字典,d2是我的新字典)。

I want to iterate over these dictionaries such that, if the key is same check for its value (nested dict), e.g. when I find key 'a'in d2, I will check whether there is 'b', if yes check value of 'cs'(changed from 10to 30), if this value is changed I want to print it.

我想遍历这些字典,如果键是相同的,检查它的值(嵌套字典),例如,当我找到 key 'a'in 时d2,我会检查是否有'b',如果有,检查值'cs'(从10改为30),如果这个值改变了我想打印它。

Another case is, I want to get key 'newa'from d2as the newly added key.

另一种情况是,我想重点'newa'd2为新添加的关键。

Hence, after iterating through these 2 dicts, this is the expected output:

因此,在遍历这 2 个 dicts 之后,这是预期的输出:

"d2" has new key "newa"
Value of "cs" is changed from 10 to 30 of key "b" which is of key "a"

I have the following code with me, I am trying with many loops which are not working though, but is not a good option too, hence I am looking to find whether I can get expected output with a recursive piece of code.

我有以下代码,我正在尝试许多无法正常工作的循环,但也不是一个好的选择,因此我正在寻找是否可以通过递归代码获得预期的输出。

for k, v in d1.iteritems():
    for k1, v1 in d2.iteritems():
        if k is k1:
            print k
            for k2 in v:
                for k3 in v1:
                    if k2 is k3:
                        print k2, "sub key matched"

        else:
            print "sorry no match found"

采纳答案by venpa

comparing 2 dictionaries using recursion:

使用递归比较 2 个字典:

Edited for python 3:

为python 3编辑:

d1= {'a':{'b':{'cs':10},'d':{'cs':20}}}
d2= {'a':{'b':{'cs':30} ,'d':{'cs':20}},'newa':{'q':{'cs':50}}}

def findDiff(d1, d2, path=""):
    for k in d1:
        if (k not in d2):
            print (path, ":")
            print (k + " as key not in d2", "\n")
        else:
            if type(d1[k]) is dict:
                if path == "":
                    path = k
                else:
                    path = path + "->" + k
                findDiff(d1[k],d2[k], path)
            else:
                if d1[k] != d2[k]:
                    print (path, ":")
                    print (" - ", k," : ", d1[k])
                    print (" + ", k," : ", d2[k])

print ("comparing d1 to d2:")
print (findDiff(d1,d2))
print ("comparing d2 to d1:")
print (findDiff(d2,d1))

python 2 code:

蟒蛇2代码:

d1= {'a':{'b':{'cs':10},'d':{'cs':20}}}
d2= {'a':{'b':{'cs':30} ,'d':{'cs':20}},'newa':{'q':{'cs':50}}}

def findDiff(d1, d2, path=""):
    for k in d1.keys():
        if not d2.has_key(k):
            print path, ":"
            print k + " as key not in d2", "\n"
        else:
            if type(d1[k]) is dict:
                if path == "":
                    path = k
                else:
                    path = path + "->" + k
                findDiff(d1[k],d2[k], path)
            else:
                if d1[k] != d2[k]:
                    print path, ":"
                    print " - ", k," : ", d1[k]
                    print " + ", k," : ", d2[k] 

print "comparing d1 to d2:"
print findDiff(d1,d2)
print "comparing d2 to d1:"
print findDiff(d2,d1)

Output:

输出:

comparing d1 to d2:
a->b :
 -  cs  :  10
 +  cs  :  30
None
comparing d2 to d1:
a->b :
 -  cs  :  30
 +  cs  :  10
a :
newa as key not in d2 

None

回答by lahu89

This should provide what you need with helpful functions:

这应该提供您需要的有用功能:

For Python 2.7

对于 Python 2.7

def isDict(obj):
    return obj.__class__.__name__ == 'dict'

def containsKeyRec(vKey, vDict):
    for curKey in vDict:
        if curKey == vKey or (isDict(vDict[curKey]) and containsKeyRec(vKey, vDict[curKey])):
            return True
    return False

def getValueRec(vKey, vDict):
    for curKey in vDict:
        if curKey == vKey:
            return vDict[curKey]
        elif isDict(vDict[curKey]) and getValueRec(vKey, vDict[curKey]):
            return containsKeyRec(vKey, vDict[curKey])
    return None

d1= {'a':{'b':{'cs':10},'d':{'cs':20}}}
d2= {'a':{'b':{'cs':30} ,'d':{'cs':20}},'newa':{'q':{'cs':50}}}

for key in d1:
    if containsKeyRec(key, d2):
        print "dict d2 contains key: " + key
        d2Value = getValueRec(key, d2)
        if d1[key] == d2Value:
            print "values are equal, d1: " + str(d1[key]) + ", d2: " + str(d2Value)
        else:
            print "values are not equal, d1: " + str(d1[key]) + ", d2: " + str(d2Value)

    else:
        print "dict d2 does not contain key: " + key

For Python 3 (or higher):

对于 Python 3(或更高版本):

def id_dict(obj):
    return obj.__class__.__name__ == 'dict'


def contains_key_rec(v_key, v_dict):
    for curKey in v_dict:
        if curKey == v_key or (id_dict(v_dict[curKey]) and contains_key_rec(v_key, v_dict[curKey])):
            return True
    return False


def get_value_rec(v_key, v_dict):
    for curKey in v_dict:
        if curKey == v_key:
            return v_dict[curKey]
        elif id_dict(v_dict[curKey]) and get_value_rec(v_key, v_dict[curKey]):
            return contains_key_rec(v_key, v_dict[curKey])
    return None


d1 = {'a': {'b': {'cs': 10}, 'd': {'cs': 20}}}
d2 = {'a': {'b': {'cs': 30}, 'd': {'cs': 20}}, 'newa': {'q': {'cs': 50}}}

for key in d1:
if contains_key_rec(key, d2):
    d2_value = get_value_rec(key, d2)
    if d1[key] == d2_value:
        print("values are equal, d1: " + str(d1[key]) + ", d2: " + str(d2_value))
        pass
    else:
        print("values are not equal:\n"
              "list1: " + str(d1[key]) + "\n" +
              "list2: " + str(d2_value))

else:
    print("dict d2 does not contain key: " + key)

回答by MohitC

Modified user3's code to make it even better

修改 user3 的代码以使其更好

d1= {'as': 1, 'a':
        {'b':
            {'cs':10,
             'qqq': {'qwe':1}
            },
            'd': {'csd':30}
        }
    }
d2= {'as': 3, 'a':
        {'b':
            {'cs':30,
             'qqq': 123
            },
            'd':{'csd':20}
        },
        'newa':
        {'q':
            {'cs':50}
        }
    }

def compare_dictionaries(dict_1, dict_2, dict_1_name, dict_2_name, path=""):
    """Compare two dictionaries recursively to find non mathcing elements

    Args:
        dict_1: dictionary 1
        dict_2: dictionary 2

    Returns:

    """
    err = ''
    key_err = ''
    value_err = ''
    old_path = path
    for k in dict_1.keys():
        path = old_path + "[%s]" % k
        if not dict_2.has_key(k):
            key_err += "Key %s%s not in %s\n" % (dict_2_name, path, dict_2_name)
        else:
            if isinstance(dict_1[k], dict) and isinstance(dict_2[k], dict):
                err += compare_dictionaries(dict_1[k],dict_2[k],'d1','d2', path)
            else:
                if dict_1[k] != dict_2[k]:
                    value_err += "Value of %s%s (%s) not same as %s%s (%s)\n"\
                        % (dict_1_name, path, dict_1[k], dict_2_name, path, dict_2[k])

    for k in dict_2.keys():
        path = old_path + "[%s]" % k
        if not dict_1.has_key(k):
            key_err += "Key %s%s not in %s\n" % (dict_2_name, path, dict_1_name)

    return key_err + value_err + err


a = compare_dictionaries(d1,d2,'d1','d2')
print a

Output:

输出:

Key d2[newa] not in d1
Value of d1[as] (1) not same as d2[as] (3)
Value of d1[a][b][cs] (10) not same as d2[a][b][cs] (30)
Value of d1[a][b][qqq] ({'qwe': 1}) not same as d2[a][b][qqq] (123)
Value of d1[a][d][csd] (30) not same as d2[a][d][csd] (20)

回答by Rishi S Kumar

For python 3 or higher, Code for comparing any data.

对于 python 3 或更高版本,用于比较任何数据的代码。

def do_compare(data1, data2, data1_name, data2_name, path=""):
    if operator.eq(data1, data2) and not path:
        log.info("Both data have same content")
    else:
        if isinstance(data1, dict) and isinstance(data2, dict):
            compare_dict(data1, data2, data1_name, data2_name, path)
        elif isinstance(data1, list) and isinstance(data2, list):
            compare_list(data1, data2, data1_name, data2_name, path)
        else:
            if data1 != data2:
                value_err = "Value of %s%s (%s) not same as %s%s (%s)\n"\
                            % (data1_name, path, data1, data2_name, path, data2)
                print (value_err)
        # findDiff(data1, data2)

def compare_dict(data1, data2, data1_name, data2_name, path):
    old_path = path
    for k in data1.keys():
        path = old_path + "[%s]" % k
        if k not in data2:
            key_err = "Key %s%s not in %s\n" % (data1_name, path, data2_name)
            print (key_err)
        else:
            do_compare(data1[k], data2[k], data1_name, data2_name, path)
    for k in data2.keys():
        path = old_path + "[%s]" % k
        if k not in data1:
            key_err = "Key %s%s not in %s\n" % (data2_name, path, data1_name)
            print (key_err)

def compare_list(data1, data2, data1_name, data2_name, path):
    data1_length = len(data1)
    data2_length = len(data2)
    old_path = path
    if data1_length != data2_length:
        value_err = "No: of items in %s%s (%s) not same as %s%s (%s)\n"\
                            % (data1_name, path, data1_length, data2_name, path, data2_length)
        print (value_err)
    for index, item in enumerate(data1):
        path = old_path + "[%s]" % index
        try:
            do_compare(data1[index], data2[index], data1_name, data2_name, path)
        except IndexError:
            pass

回答by Arran Duff

Adding a non-recursive solution.

添加非递归解决方案。

  # Non Recursively traverses through a large nested dictionary
  # Uses a queue of dicts_to_process to keep track of what needs to be traversed rather than using recursion.
  # Slightly more complex than the recursive version, but arguably better as there is no risk of stack overflow from
  # too many levels of recursion
  def get_dict_diff_non_recursive(dict1, dict2):
      dicts_to_process=[(dict1,dict2,"")]
      while dicts_to_process:
          d1,d2,current_path = dicts_to_process.pop()
          for key in d1.keys():
              current_path = os.path.join(current_path, f"{key}")
              #print(f"searching path {current_path}")
              if key not in d2 or d1[key] != d2[key]:
                  print(f"difference at {current_path}")
              if type(d1[key]) == dict:
                  dicts_to_process.append((d1[key],d2[key],current_path))
              elif type(d1[key]) == list and d1[key] and type(d1[key][0]) == dict:
                  for i in range(len(d1[key])):
                      dicts_to_process.append((d1[key][i], d2[key][i],current_path))