84

I am currently using the following function to compare dictionary values and display all the values that don't match. Is there a faster or better way to do it?

match = True
for keys in dict1:
    if dict1[keys] != dict2[keys]:
        match = False
        print keys
        print dict1[keys],
        print  '->' ,
        print dict2[keys]

Edit: Both the dicts contain the same keys.

Dag Høidahl
  • 7,873
  • 8
  • 53
  • 66
randomThought
  • 6,203
  • 15
  • 56
  • 72
  • Need a little bit of clarification...Are you trying to determine if dict1 and dict2 contain exactly the same things? Or can dict2 contain other values that are not in dict1? Also, do you need to be able to display all the keys that don't match? – Brent Writes Code Dec 15 '09 at 23:53
  • I need to be able to display all the values that dont match. Dict2 has the same keys as dict1 – randomThought Dec 15 '09 at 23:58
  • That's about what I'd write then. You can `print dict1[keys], "->", dict2[keys]` on one line. – Jason Orendorff Dec 16 '09 at 00:06
  • Here's one util for visualising differences: https://github.com/AJamesPhillips/compare/tree/master – AJP Nov 28 '13 at 22:18

9 Answers9

222

If the true intent of the question is the comparison between dicts (rather than printing differences), the answer is

dict1 == dict2

This has been mentioned before, but I felt it was slightly drowning in other bits of information. It might appear superficial, but the value comparison of dicts has actually powerful semantics. It covers

  • number of keys (if they don't match, the dicts are not equal)
  • names of keys (if they don't match, they're not equal)
  • value of each key (they have to be '==', too)

The last point again appears trivial, but is acutally interesting as it means that all of this applies recursively to nested dicts as well. E.g.

 m1 = {'f':True}
 m2 = {'f':True}
 m3 = {'a':1, 2:2, 3:m1}
 m4 = {'a':1, 2:2, 3:m2}
 m3 == m4  # True

Similar semantics exist for the comparison of lists. All of this makes it a no-brainer to e.g. compare deep Json structures, alone with a simple "==".

ThomasH
  • 22,276
  • 13
  • 61
  • 62
  • 13
    `all of this applies recursively to nested dicts as well` Is this explicitly documented, along with any limitations to a complex nested structure e.g. dict of list of dict etc.? – user Nov 05 '14 at 18:52
  • 1
    I can't think off the top of my head about a single piece of documentation that talks exactly about this. My answer is from experience and experimentation. I recommend the same to you, e.g. test comparisons of some data structures that are interesting to you. – ThomasH Nov 10 '14 at 11:39
  • 7
    Wow, this is true - `==` does deep compare, at least for basic types! Try in REPL: `a = {"a": False, 'b':[1,2,{'foo':'bar','zamboni':True,'cheese':[False,[]]},3],'c':None}; b = {'c':None, 'a': False, 'b':[1,2,{'cheese':[False,[]],'foo':'bar','zamboni':True},3]}; a == b`. Cool! – ACK_stoverflow Aug 19 '16 at 16:08
  • 6
    I did not find official document for the `applies recursively to nested dicts as well` part either, but, for what its worth, the `==` equality comparison of 2 dicts are officially promised in the [last sentence of this section](https://docs.python.org/2/library/stdtypes.html#mapping-types-dict), and also you can search "Mappings (instances of dict) compare equal" in this [separated section](https://docs.python.org/2/reference/expressions.html#value-comparisons). – RayLuo Feb 17 '17 at 21:07
  • 10
    If each element gets the `==` test and any element can be a dict, then of course it applies recursively to nested dicts. – EL_DON Apr 27 '18 at 16:37
  • Also a good time to remember what types have orderings and what do not. lists and tuples do, sets do not. So - yes - this works! – webelo Jun 08 '20 at 14:16
  • Wow not documented? Why is Python documentation so bad? – Joe Coder Sep 14 '20 at 23:10
  • @JoeCoder: This **is** documented. From the [official docs](https://docs.python.org/3/library/stdtypes.html#mapping-types-dict): ```Dictionaries compare equal if and only if they have the same (key, value) pairs (regardless of ordering). Order comparisons (‘<’, ‘<=’, ‘>=’, ‘>’) raise TypeError.``` – MestreLion Oct 25 '21 at 23:19
  • 1
    @MestreLion It's ambiguous as to whether that is shallow or deep equality. My quibble with Python, coming from the excellent documentation of the Java ecosystems, is that the documentation seems unstructured and vague, to where I have to experimentally disambiguate the described behavior as others have mentioned in this comment thread. – Joe Coder Oct 26 '21 at 02:54
  • @JoeCoder: it's not ambiguous: as @EL_DON said before, if _each_ `(key, value)` pair is tested for equality, then all values will also be tested. Recursion is a logical consequence of that sentence. What **else** could it mean? In other words: what would be a "shallow" equality? – MestreLion Oct 26 '21 at 13:51
  • @MestreLion I cannot tell from reading that blurb whether "same" refers to an identity or value comparison. I also would not assume a deep comparison; languages like JavaScript & Java perform shallow comparisons, meaning that constituent objects are compared by identity. For deep comparisons of arbitrary complexity, they require 3rd party libraries which allow you to alter the behavior with parameters. Does Python builtin comparison have limitations? Can handle large cyclical reference graph? Ability to consider None values same as missing value? No information. – Joe Coder Oct 26 '21 at 15:42
  • @JoeCoder: Oh, I see. The ambiguity could arise from the word _"same"_ in `same (key, value) pairs`, if that means an identity or an equality test. In Python they're distinct, `is` for identity and `==` equality. The equality of an object is customizable, and it defaults to identity if not defined. Since a `dict` _does_ define an equality (as stated), it would be very surprising if it _didn't_ use equality for members when testing its own equality. – MestreLion Oct 26 '21 at 16:11
  • No I get that; Java has similar convention (`==` for identity, `obj.equals` for value/custom). We already know we are using the latter. The ambiguity is in the nested behavior, and for people coming from other major P/Ls, the Python behavior is surprising and possibly raises an eyebrow because to handle arbitrary complexity is a major feature and yet the description of this is 1-2 sentences. – Joe Coder Oct 26 '21 at 19:55
  • What about ```In [13]: m1 = {'f':True}; m2 = {'f':1}; m3 = {'a':1, 2:2, 3:m1}; m4 = {'a':1, 2:2, 3:m2}; m3 == m4; # True Out[13]: True```? I understand that equality `==` in python is not identity. But I'd like to point out that using the `==` operator for nested dictionaries might result in some unexpected (albeit documented) edge cases. – luca.giovagnoli Jan 20 '22 at 17:42
  • @luca.giovagnoli It has nothing to do with comparing dictionaries. In Python simply `True == 1` is true (as is `False == 0`). – ThomasH Jan 21 '22 at 16:33
49

If the dicts have identical sets of keys and you need all those prints for any value difference, there isn't much you can do; maybe something like:

diffkeys = [k for k in dict1 if dict1[k] != dict2[k]]
for k in diffkeys:
  print k, ':', dict1[k], '->', dict2[k]

pretty much equivalent to what you have, but you might get nicer presentation for example by sorting diffkeys before you loop on it.

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • 2
    it is safer to do `dict2.get(k)` as it might not be there. Also if you want to really compare both ways, you might want to do the other way around as well as it will only detect from dict1 keys point of view, eg. when dict2 has a lot more attributes – rendybjunior Feb 06 '20 at 23:18
16

You can use sets for this too

>>> a = {'x': 1, 'y': 2}
>>> b = {'y': 2, 'x': 1}
>>> set(a.iteritems())-set(b.iteritems())
set([])
>>> a['y']=3
>>> set(a.iteritems())-set(b.iteritems())
set([('y', 3)])
>>> set(b.iteritems())-set(a.iteritems())
set([('y', 2)])
>>> set(b.iteritems())^set(a.iteritems())
set([('y', 3), ('y', 2)])
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
  • 3
    This works really well unless one of the values is unhashable (e.g., a `list`). `set({'x': []}.iteritems())` will raise a `TypeError`. –  Aug 24 '13 at 21:17
  • 1
    @user212218 - For dictionary items with unhashable objects, see here: http://stackoverflow.com/questions/43504568/compare-dictionaries-with-unhashable-or-uncomparable-values-e-g-lists-or-data – Afflatus Apr 19 '17 at 21:45
9

Uhm, you are describing dict1 == dict2 ( check if boths dicts are equal )

But what your code does is all( dict1[k]==dict2[k] for k in dict1 ) ( check if all entries in dict1 are equal to those in dict2 )

Jochen Ritzel
  • 104,512
  • 31
  • 200
  • 194
  • 5
    Which misses the case where dict2 has *additional* keys over dict1, so they're actually not behaving the same. – ThomasH Apr 12 '11 at 12:59
2

Not sure if this helps but in my app I had to check if a dictionary has changed.

Doing this will not work since basically it's still the same object:

val={'A':1,'B':2}
old_val=val

val['A']=10
if old_val != val:
  print('changed')

Using copy/deepcopy works:

import copy
val={'A':1,'B':2}
old_val=copy.deepcopy(val)

val['A']=10
if old_val != val:
  print('changed')
erazor
  • 31
  • 4
2

If your values are hashable (ie. strings), then you can simply compare the ItemsView of the two dicts.

https://docs.python.org/3/library/stdtypes.html#dict-views

set_with_unique_key_value_pairs = dict1.items() ^ dict2.items()
set_with_matching_key_value_pairs = dict1.items() & dict2.items()

Any set operations are available to you.

Since you might not care about keys in this case, you can also just use the ValuesView (again, provided the values are hashable).

set_with_matching_values = dict1.values() & dict2.values()
Tyler Donaldson
  • 426
  • 3
  • 6
1

If you're just comparing for equality, you can just do this:

if not dict1 == dict2:
    match = False

Otherwise, the only major problem I see is that you're going to get a KeyError if there is a key in dict1 that is not in dict2, so you may want to do something like this:

for key in dict1:
    if not key in dict2 or dict1[key] != dict2[key]:
        match = False

You could compress this into a comprehension to just get the list of keys that don't match too:

mismatch_keys = [key for key in x if not key in y or x[key] != y[key]]
match = not bool(mismatch_keys) #If the list is not empty, they don't match 
for key in mismatch_keys:
    print key
    print '%s -> %s' % (dict1[key],dict2[key])

The only other optimization I can think of might be to use "len(dict)" to figure out which dict has fewer entries and loop through that one first to have the shortest loop possible.

Brent Writes Code
  • 19,075
  • 7
  • 52
  • 56
1
>>> a = {'x': 1, 'y': 2}
>>> b = {'y': 2, 'x': 1}
>>> print a == b
True
>>> c = {'z': 1}
>>> print a == c
False
>>> 
yedpodtrzitko
  • 9,035
  • 2
  • 40
  • 42
0

If your dictionaries are deeply nested and if they contain different types of collections, you could convert them to json string and compare.

import json
match = (json.dumps(dict1) == json.dumps(dict2))

caveat- this solution may not work if your dictionaries have binary strings in the values as this is not json serializable

josliber
  • 43,891
  • 12
  • 98
  • 133
user3283069
  • 355
  • 1
  • 6
  • 4
    Note -- at least in Python 2.6, order isn't guaranteed to be consistent, so it's possible for the same key:values in both dicts to generate different json.dumps() if the keys are in a different order and comprised of certain strings. (Honestly couldn't suss out the exact rules, but in my two dicts, changing a leading "b" to a "v" in the same key on both sides of the comparison causes the resulting strings to differ, repeatably.) – DreadPirateShawn Apr 30 '16 at 05:57
  • It would be better to use a deep equality function. It would be cheaper and actually work properly. – Dan D. Nov 28 '16 at 09:49
  • @DanD: Can you elaborate what is deep equality function in this case? – JavaSa Sep 27 '20 at 17:46