0

I have two functions that must return the same dictionary (one is the old method and the other one, is a new method that will replace the old one). As I am testing that the new function gives me the same result as the old one. I am trying to create a program that takes as input, two dictionaries and verifies that they have the same information and tells me any differences between them. My scope is to understand which info of the new function is wrong.

Those two dictionaries can change each time. They could have keys that contains just a string or a dictionary or a list of dictionaries. Also, those list of dictionaries could contain keys that have string values or a dictionary or another list of dictionaries. The dictionaries can arbitrarily complex.

I have tried this, but the result is not good. attr_prod is an example of the dictionary that returns the old function. attr_test is an example of the dictionary that returns the new function.

def main():
    attr_prod = {
            'key1': 'a',
            'key2': [ {
                        'key3': 'n1',
                        'key4': 'n2',
                        'key5': [ {
                                    'key6': 'n1',
                                    'key7': 'n2',
                                    'key8': {
                                        'key10': 'g1',
                                        'key11': 'g2',
                                        'key12': 'g3'
                                        }
                                    },
                                    {
                                    'key6': 'n5',
                                    'key7': 'n6',
                                    'key8': {
                                        'key10': 'g4',
                                        'key11': 'g5',
                                        'key12': 'g6'
                                    }
                                 }
                                ]
                        }
                    ],
            'key20': {
                        'key21': 'g4',
                        'key22': 'g5',
                        'key23': 'g6'
                    }
        }

    attr_test = {
            'key1': 'a',
            'key2': [ {
                        'key3': 'different info',
                        'key4': 'n2',
                        'key5': [   {
                                    'key6': 'n1',
                                    'key7': 'n2',
                                    'key8': {
                                        'key10': 'g1',
                                        'key11': 'g2',
                                        'key12': 'g3'
                                        }
                                    },
                                    {
                                    'key6': 'n5',
                                    'key7': 'n6',
                                    'key8': {
                                        'key10': 'different info',
                                        'key11': 'g5',
                                        'key12': 'g6'
                                        }
                                    },
                                    {
                                    'key6': 'this is new',
                                    'key7': 'this is new',
                                    'key8': {
                                        'key10': 'new',
                                        'key11': 'new',
                                        'key12': 'new'
                                    }
                                }
                            ]
                        }
                    ],
            'key20': {
                        'key21': 'g4',
                        'key22': 'different info',
                        'key23': 'g6'
                    }
        }

    for key, value_prod in attr_prod.items():
            if isinstance(value_prod, basestring):
                check(key, value_prod, attr_test[key])
            elif isinstance(value_prod, list):
                n_rec = 0
                for rec in value_prod:
#                   if isinstance(rec, basestring):
#                       check(key, rec, attr_test[key][n_rec])
                    print rec
                    if isinstance(rec, dict):
                        for key2, value_prod2 in rec.items():
                            if isinstance(value_prod2, basestring):
                                check(key, value_prod2, attr_test[key][n_rec][key2])
                            elif isinstance(value_prod2, list):
                                n_rec2 = 0
                                for rec2 in value_prod2:
                                    for key3, value_prod3 in rec2.items():
                                        if isinstance(value_prod3, basestring):
                                            check(key, value_prod3, attr_test[key][n_rec][key2][n_rec2][key3])
                                n_rec2 += 1
                    n_rec += 1


def check(key, value_prod, value_test):
    if value_test != value_prod:
        print "KO key: %s test: -%s- prod: -%s-" % (key, value_test, value_prod)
    

Marc
  • 25
  • 4
  • 1
    What exactly do you mean by "the result is not good"? – mkrieger1 Feb 25 '21 at 20:13
  • I mean that I cannot understand where is the difference. It prints some keys and values, but I cannot understand what is wrong with the new function or where is in the dictionary. the correct result must be the dictionary attr_prod (the dictionary that returns the old function) and I need to understand what data of the new function is not correct (the dictionary that returns the new function is attr_test). – Marc Feb 25 '21 at 20:18
  • 2
    `dict1 == dict2` will suffice because it's a deep compare. If the condition is false, you can walk through both dicts to see what's different. But you have to specify what "different" means. For example, the value of the "key2" in both dictionaries is different, so you could print those two lists. Or if you dig deeper you can see that they're the same except from one value and print only that. – Reti43 Feb 25 '21 at 20:19
  • But i need to see a deep level. in the sense 'key3' has different info but key 4 is ok, then I don't want to print key 4. key10 have different info, then i want to print key10 and the different values. – Marc Feb 25 '21 at 20:23
  • One way to do it would might be by tracking the changes made to the dictionaries as shown in this [answer](https://stackoverflow.com/a/5904056/355230) of mine to a related question. Otherwise, I don't think what you want to do is feasible in any kind of general sense. – martineau Feb 25 '21 at 20:28
  • It would be very good to have key2-> key 3-> old: n1, new:different info – Marc Feb 25 '21 at 20:29
  • Are you sure all keys are the same or they may be differences in keys too? – Mactov Feb 25 '21 at 20:59
  • The names of the keys in both dictionaries are the same. But keys could be or not in the second dictionary (attr_test). Maybe in the second dictionary (attr_test) some of them are missing. – Marc Feb 25 '21 at 22:28

1 Answers1

1

I would suggest making a recursive dictionary comparison function as a generator (thus allowing you to efficiently detect at least one difference using the next function):

def compareDict(before,after):
    for k,old in before.items():
        if k not in after: yield f"{k}: deleted";continue
        new = after[k]
        if type(old) != type(new):
            yield f"{k}: changed from: {old}"
            yield f"{k}: changed   to: {new}"
        elif isinstance(old,dict):
            yield from (f"{k}:{change}" for change in compareDict(old,new))
        elif isinstance(old,(tuple,list)):
            b = {f"[{i}]":v for i,v in enumerate(old)}
            a = {f"[{i}]":v for i,v in enumerate(new)}
            yield from (f"{k}:{change}" for change in compareDict(b,a))
        elif old != new:
            yield f"{k}: changed from: {old}"
            yield f"{k}: changed   to: {new}"
    for k,new in after.items():
        if k not in before: yield f"{k}: added: {new}"

output:

for change in compareDict(attr_prod,attr_test): print(change)

key2:[0]:key3: changed from: n1
key2:[0]:key3: changed   to: different info
key2:[0]:key5:[1]:key8:key10: changed from: g4
key2:[0]:key5:[1]:key8:key10: changed   to: different info
key2:[0]:key5:[2]: added: {'key6': 'this is new', 'key7': 'this is new', 'key8': {'key10': 'new', 'key11': 'new', 'key12': 'new'}}
key20:key22: changed from: g5
key20:key22: changed   to: different info
Alain T.
  • 40,517
  • 4
  • 31
  • 51