Twee woordenboeken vergelijken en controleren hoeveel (sleutel, waarde) paren gelijk zijn

Ik heb twee woordenboeken, maar ter vereenvoudiging neem ik deze twee:

>>> x = dict(a=1, b=2)
>>> y = dict(a=2, b=2)

Nu wil ik vergelijken of elk key, value-paar in xdezelfde corresponderende waarde heeft in y. Dus ik schreef dit:

>>> for x_values, y_values in zip(x.iteritems(), y.iteritems()):
        if x_values == y_values:
            print 'Ok', x_values, y_values
        else:
            print 'Not', x_values, y_values

En het werkt omdat een tuplewordt geretourneerd en vervolgens wordt vergeleken voor gelijkheid.

Mijn vragen:

Is dit correct? Is er een beteremanier om dit te doen? Beter niet in snelheid, ik heb het over code-elegantie.

UPDATE: ik ben vergeten te vermelden dat ik moet controleren hoeveel key, value-paren gelijk zijn.


Antwoord 1, autoriteit 100%

Als je wilt weten hoeveel waarden overeenkomen in beide woordenboeken, had je dat moeten zeggen 🙂

Misschien zoiets als dit:

shared_items = {k: x[k] for k in x if k in y and x[k] == y[k]}
print len(shared_items)

Antwoord 2, autoriteit 94%

Wat je wilt doen is gewoon x==y

Wat je doet is geen goed idee, want de items in een woordenboek horen geen volgorde te hebben. Mogelijk vergelijkt u [('a',1),('b',1)]met [('b',1), ('a',1)](dezelfde woordenboeken, andere volgorde).

Zie bijvoorbeeld dit:

>>> x = dict(a=2, b=2,c=3, d=4)
>>> x
{'a': 2, 'c': 3, 'b': 2, 'd': 4}
>>> y = dict(b=2,c=3, d=4)
>>> y
{'c': 3, 'b': 2, 'd': 4}
>>> zip(x.iteritems(), y.iteritems())
[(('a', 2), ('c', 3)), (('c', 3), ('b', 2)), (('b', 2), ('d', 4))]

Het verschil is slechts één item, maar uw algoritme zal zien dat alle items verschillend


3, Autoriteit 91%

def dict_compare(d1, d2):
    d1_keys = set(d1.keys())
    d2_keys = set(d2.keys())
    shared_keys = d1_keys.intersection(d2_keys)
    added = d1_keys - d2_keys
    removed = d2_keys - d1_keys
    modified = {o : (d1[o], d2[o]) for o in shared_keys if d1[o] != d2[o]}
    same = set(o for o in shared_keys if d1[o] == d2[o])
    return added, removed, modified, same
x = dict(a=1, b=2)
y = dict(a=2, b=2)
added, removed, modified, same = dict_compare(x, y)

Antwoord 4, autoriteit 36%

Aangezien het lijkt alsof niemand deepdiffheeft genoemd, zal ik het hier toevoegen voor de volledigheid. Ik vind het erg handig om diff van (geneste) objecten in het algemeen te krijgen:

Installatie

pip install deepdiff

Voorbeeldcode

import deepdiff
import json
dict_1 = {
    "a": 1,
    "nested": {
        "b": 1,
    }
}
dict_2 = {
    "a": 2,
    "nested": {
        "b": 2,
    }
}
diff = deepdiff.DeepDiff(dict_1, dict_2)
print(json.dumps(diff, indent=4))

Uitvoer

{
    "values_changed": {
        "root['a']": {
            "new_value": 2,
            "old_value": 1
        },
        "root['nested']['b']": {
            "new_value": 2,
            "old_value": 1
        }
    }
}

Opmerking over het mooi afdrukken van het resultaat voor inspectie: de bovenstaande code werkt als beide dictaten dezelfde attribuutsleutels hebben (met mogelijk verschillende attribuutwaarden zoals in het voorbeeld). Als er echter een "extra"-attribuut aanwezig is, is een van de dicts, json.dumps()mislukt met

TypeError: Object of type PrettyOrderedSet is not JSON serializable

Oplossing: gebruik diff.to_json()en json.loads()/ json.dumps()om mooi af te drukken:

import deepdiff
import json
dict_1 = {
    "a": 1,
    "nested": {
        "b": 1,
    },
    "extra": 3
}
dict_2 = {
    "a": 2,
    "nested": {
        "b": 2,
    }
}
diff = deepdiff.DeepDiff(dict_1, dict_2)
print(json.dumps(json.loads(diff.to_json()), indent=4))  

Uitvoer:

{
    "dictionary_item_removed": [
        "root['extra']"
    ],
    "values_changed": {
        "root['a']": {
            "new_value": 2,
            "old_value": 1
        },
        "root['nested']['b']": {
            "new_value": 2,
            "old_value": 1
        }
    }
}

Alternatief: gebruik pprint, resulteert in een andere opmaak:

import pprint
# same code as above
pprint.pprint(diff, indent=4)

Uitvoer:

{   'dictionary_item_removed': [root['extra']],
    'values_changed': {   "root['a']": {   'new_value': 2,
                                           'old_value': 1},
                          "root['nested']['b']": {   'new_value': 2,
                                                     'old_value': 1}}}

Antwoord 5, autoriteit 28%

Ik ben nieuw bij python, maar ik heb uiteindelijk iets gedaan dat lijkt op @mouad

unmatched_item = set(dict_1.items()) ^ set(dict_2.items())
len(unmatched_item) # should be 0

De XOR-operator (^) moet alle elementen van het dictaat verwijderen als ze in beide dictaten hetzelfde zijn.


Antwoord 6, autoriteit 23%

Gebruik gewoon:

assert cmp(dict1, dict2) == 0

Antwoord 7, autoriteit 4%

@mouad’s antwoord is leuk als je ervan uitgaat dat beide woordenboeken alleen eenvoudige waarden bevatten. Als u echter woordenboeken heeft die woordenboeken bevatten, krijgt u een uitzondering omdat woordenboeken niet kunnen worden gehasht.

Zo uit mijn hoofd, zoiets zou kunnen werken:

def compare_dictionaries(dict1, dict2):
     if dict1 is None or dict2 is None:
        print('Nones')
        return False
     if (not isinstance(dict1, dict)) or (not isinstance(dict2, dict)):
        print('Not dict')
        return False
     shared_keys = set(dict1.keys()) & set(dict2.keys())
     if not ( len(shared_keys) == len(dict1.keys()) and len(shared_keys) == len(dict2.keys())):
        print('Not all keys are shared')
        return False
     dicts_are_equal = True
     for key in dict1.keys():
         if isinstance(dict1[key], dict) or isinstance(dict2[key], dict):
             dicts_are_equal = dicts_are_equal and compare_dictionaries(dict1[key], dict2[key])
         else:
             dicts_are_equal = dicts_are_equal and all(atleast_1d(dict1[key] == dict2[key]))
     return dicts_are_equal

Antwoord 8, autoriteit 3%

Nog een andere mogelijkheid, tot de laatste noot van de OP, is om de hashes (SHAof MD) van de dicts die als JSON zijn gedumpt, te vergelijken. De manier waarop hashes worden geconstrueerd, garandeert dat als ze gelijk zijn, de bronstrings ook gelijk zijn. Dit is erg snel en wiskundig verantwoord.

import json
import hashlib
def hash_dict(d):
    return hashlib.sha1(json.dumps(d, sort_keys=True)).hexdigest()
x = dict(a=1, b=2)
y = dict(a=2, b=2)
z = dict(a=1, b=2)
print(hash_dict(x) == hash_dict(y))
print(hash_dict(x) == hash_dict(z))

Antwoord 9, autoriteit 3%

De functie is prima IMO, duidelijk en intuïtief. Maar om je (nog) een antwoord te geven, hier is mijn doel:

def compare_dict(dict1, dict2):
    for x1 in dict1.keys():
        z = dict1.get(x1) == dict2.get(x1)
        if not z:
            print('key', x1)
            print('value A', dict1.get(x1), '\nvalue B', dict2.get(x1))
            print('-----\n')

Kan nuttig zijn voor jou of voor iemand anders..

BEWERKEN:

Ik heb een recursieve versie van de bovenstaande gemaakt. Ik heb dat niet gezien in de andere antwoorden

def compare_dict(a, b):
    # Compared two dictionaries..
    # Posts things that are not equal..
    res_compare = []
    for k in set(list(a.keys()) + list(b.keys())):
        if isinstance(a[k], dict):
            z0 = compare_dict(a[k], b[k])
        else:
            z0 = a[k] == b[k]
        z0_bool = np.all(z0)
        res_compare.append(z0_bool)
        if not z0_bool:
            print(k, a[k], b[k])
    return np.all(res_compare)

Antwoord 10, autoriteit 2%

Om te testen of twee dictaten gelijk zijn in sleutels en waarden:

def dicts_equal(d1,d2):
    """ return True if all keys and values are the same """
    return all(k in d2 and d1[k] == d2[k]
               for k in d1) \
        and all(k in d1 and d1[k] == d2[k]
               for k in d2)

Als je de waarden wilt retourneren die verschillen, schrijf het dan anders:

def dict1_minus_d2(d1, d2):
    """ return the subset of d1 where the keys don't exist in d2 or
        the values in d2 are different, as a dict """
    return {k,v for k,v in d1.items() if k in d2 and v == d2[k]}

Je zou het twee keer moeten aanroepen, d.w.z.

dict1_minus_d2(d1,d2).extend(dict1_minus_d2(d2,d1))

Antwoord 11, autoriteit 2%

Een simpele vergelijking met == zou tegenwoordig voldoende moeten zijn (python 3.8). Ook als je dezelfde dictaten in een andere volgorde vergelijkt (laatste voorbeeld). Het beste is dat je hiervoor geen pakket van derden nodig hebt.

a = {'one': 'dog', 'two': 'cat', 'three': 'mouse'}
b = {'one': 'dog', 'two': 'cat', 'three': 'mouse'}
c = {'one': 'dog', 'two': 'cat', 'three': 'mouse'}
d = {'one': 'dog', 'two': 'cat', 'three': 'mouse', 'four': 'fish'}
e = {'one': 'cat', 'two': 'dog', 'three': 'mouse'}
f = {'one': 'dog', 'two': 'cat', 'three': 'mouse'}
g = {'two': 'cat', 'one': 'dog', 'three': 'mouse'}
h = {'one': 'dog', 'two': 'cat', 'three': 'mouse'}
print(a == b) # True
print(c == d) # False
print(e == f) # False
print(g == h) # True

Antwoord 12

Code

def equal(a, b):
    type_a = type(a)
    type_b = type(b)
    if type_a != type_b:
        return False
    if isinstance(a, dict):
        if len(a) != len(b):
            return False
        for key in a:
            if key not in b:
                return False
            if not equal(a[key], b[key]):
                return False
        return True
    elif isinstance(a, list):
        if len(a) != len(b):
            return False
        while len(a):
            x = a.pop()
            index = indexof(x, b)
            if index == -1:
                return False
            del b[index]
        return True
    else:
        return a == b
def indexof(x, a):
    for i in range(len(a)):
        if equal(x, a[i]):
            return i
    return -1

Test

>>> a = {
    'number': 1,
    'list': ['one', 'two']
}
>>> b = {
    'list': ['two', 'one'],
    'number': 1
}
>>> equal(a, b)
True

Antwoord 13

Ik gebruik deze oplossing die perfect werkt voor mij in Python 3


import logging
log = logging.getLogger(__name__)
...
    def deep_compare(self,left, right, level=0):
        if type(left) != type(right):
            log.info("Exit 1 - Different types")
            return False
        elif type(left) is dict:
            # Dict comparison
            for key in left:
                if key not in right:
                    log.info("Exit 2 - missing {} in right".format(key))
                    return False
                else:
                    if not deep_compare(left[str(key)], right[str(key)], level +1 ):
                        log.info("Exit 3 - different children")
                        return False
            return True
        elif type(left) is list:
            # List comparison
            for key in left:
                if key not in right:
                    log.info("Exit 4 - missing {} in right".format(key))
                    return False
                else:
                    if not deep_compare(left[left.index(key)], right[right.index(key)], level +1 ):
                        log.info("Exit 5 - different children")
                        return False
            return True
        else:
            # Other comparison
            return left == right
        return False

Het vergelijkt dict, lijst en alle andere typen die de operator “==” zelf implementeren.
Als je iets anders anders wilt vergelijken, moet je een nieuwe tak toevoegen in de “if tree”.

Hopelijk helpt dat.


Antwoord 14

voor python3:

data_set_a = dict_a.items()
data_set_b = dict_b.items()
difference_set = data_set_a ^ data_set_b

Antwoord 15

In PyUnit is er een methode die woordenboeken prachtig vergelijkt. Ik heb het getest met behulp van de volgende twee woordenboeken en het doet precies wat je zoekt.

d1 = {1: "value1",
      2: [{"subKey1":"subValue1",
           "subKey2":"subValue2"}]}
d2 = {1: "value1",
      2: [{"subKey2":"subValue2",
           "subKey1": "subValue1"}]
      }
def assertDictEqual(self, d1, d2, msg=None):
        self.assertIsInstance(d1, dict, 'First argument is not a dictionary')
        self.assertIsInstance(d2, dict, 'Second argument is not a dictionary')
        if d1 != d2:
            standardMsg = '%s != %s' % (safe_repr(d1, True), safe_repr(d2, True))
            diff = ('\n' + '\n'.join(difflib.ndiff(
                           pprint.pformat(d1).splitlines(),
                           pprint.pformat(d2).splitlines())))
            standardMsg = self._truncateMessage(standardMsg, diff)
            self.fail(self._formatMessage(msg, standardMsg))

Ik beveel niet het importeren van het importeren van unittestin uw productiecode. Mijn gedachte is de bron in Pyunit kan opnieuw worden gereageerd om in productie te lopen. Het gebruikt pprintdie de woordenboeken “behoorlijk afdrukken”. Lijkt vrij eenvoudig om deze code aan te passen als “productie klaar”.


16

laat in mijn reactie is beter dan nooit!

Vergelijk Not_Equal is efficiënter dan gelijk te vergelijken. Als zodanig zijn twee dictten niet gelijk als de sleutelwaarden in één dict niet worden gevonden in het andere dict. De onderstaande code houdt rekening met het vergelijken van standaard dict en wordt dus gebruikt in plaats van getitem [].

Een soort willekeurige waarde gebruiken als standaard in de GET-oproep die is opgehaald aan de toets die wordt opgehaald – voor het geval de Dictten een Geen hebben als waarde in één dict en die sleutel niet in de ander bestaat. Ook wordt de GET! = Conditie gecontroleerd vóór de niet-in-voorwaarde voor efficiëntie omdat u tegelijkertijd de toetsen en waarden van beide zijden aan het doen bent.

def Dicts_Not_Equal(first,second):
    """ return True if both do not have same length or if any keys and values are not the same """
    if len(first) == len(second): 
        for k in first:
            if first.get(k) != second.get(k,k) or k not in second: return (True)
        for k in second:         
            if first.get(k,k) != second.get(k) or k not in first: return (True)
        return (False)   
    return (True)

17

Waarom niet alleen geëerbiedigd door een woordenboek en de andere in het proces controleren (aannemende woordenboeken hebben dezelfde sleutels)?

x = dict(a=1, b=2)
y = dict(a=2, b=2)
for key, val in x.items():
    if val == y[key]:
        print ('Ok', val, y[key])
    else:
        print ('Not', val, y[key])

Uitgang:

Not 1 2
Ok 2 2

18

De eenvoudigste manier (en een van de robuuster daarop) om een ​​diepe vergelijking van twee woordenboeken te doen, is om ze in JSON-formaat te serialiseren, de sleutels te sorteren en de string-resultaten te vergelijken:

import json
if json.dumps(x, sort_keys=True) == json.dumps(y, sort_keys=True):
   ... Do something ...

19

>>> hash_1
{'a': 'foo', 'b': 'bar'}
>>> hash_2
{'a': 'foo', 'b': 'bar'}
>>> set_1 = set (hash_1.iteritems())
>>> set_1
set([('a', 'foo'), ('b', 'bar')])
>>> set_2 = set (hash_2.iteritems())
>>> set_2
set([('a', 'foo'), ('b', 'bar')])
>>> len (set_1.difference(set_2))
0
>>> if (len(set_1.difference(set_2)) | len(set_2.difference(set_1))) == False:
...    print "The two hashes match."
...
The two hashes match.
>>> hash_2['c'] = 'baz'
>>> hash_2
{'a': 'foo', 'c': 'baz', 'b': 'bar'}
>>> if (len(set_1.difference(set_2)) | len(set_2.difference(set_1))) == False:
...     print "The two hashes match."
...
>>>
>>> hash_2.pop('c')
'baz'

Hier is een andere optie:

>>> id(hash_1)
140640738806240
>>> id(hash_2)
140640738994848

Dus als je ziet dat de twee id’s anders zijn. Maar de Rijke vergelijkingsoperators lijken de kunst te doen:

>>> hash_1 == hash_2
True
>>>
>>> hash_2
{'a': 'foo', 'b': 'bar'}
>>> set_2 = set (hash_2.iteritems())
>>> if (len(set_1.difference(set_2)) | len(set_2.difference(set_1))) == False:
...     print "The two hashes match."
...
The two hashes match.
>>>

Antwoord 20

zie woordenboekweergave-objecten:
https://docs.python.org/2/library/stdtypes.html# dictee

Op deze manier kun je dictView2 aftrekken van dictView1 en het zal een set sleutel/waarde-paren retourneren die anders zijn in dictView2:

original = {'one':1,'two':2,'ACTION':'ADD'}
originalView=original.viewitems()
updatedDict = {'one':1,'two':2,'ACTION':'REPLACE'}
updatedDictView=updatedDict.viewitems()
delta=original | updatedDict
print delta
>>set([('ACTION', 'REPLACE')])

U kunt deze woordenboekweergave-objecten kruisen, samenvoegen, verschillen (hierboven weergegeven), symmetrische verschillen.
Beter? Sneller? – niet zeker, maar onderdeel van de standaardbibliotheek – wat het een groot pluspunt maakt voor draagbaarheid


Antwoord 21

De onderstaande code helpt je om de lijst met dict in python te vergelijken

def compate_generic_types(object1, object2):
    if isinstance(object1, str) and isinstance(object2, str):
        return object1 == object2
    elif isinstance(object1, unicode) and isinstance(object2, unicode):
        return object1 == object2
    elif isinstance(object1, bool) and isinstance(object2, bool):
        return object1 == object2
    elif isinstance(object1, int) and isinstance(object2, int):
        return object1 == object2
    elif isinstance(object1, float) and isinstance(object2, float):
        return object1 == object2
    elif isinstance(object1, float) and isinstance(object2, int):
        return object1 == float(object2)
    elif isinstance(object1, int) and isinstance(object2, float):
        return float(object1) == object2
    return True
def deep_list_compare(object1, object2):
    retval = True
    count = len(object1)
    object1 = sorted(object1)
    object2 = sorted(object2)
    for x in range(count):
        if isinstance(object1[x], dict) and isinstance(object2[x], dict):
            retval = deep_dict_compare(object1[x], object2[x])
            if retval is False:
                print "Unable to match [{0}] element in list".format(x)
                return False
        elif isinstance(object1[x], list) and isinstance(object2[x], list):
            retval = deep_list_compare(object1[x], object2[x])
            if retval is False:
                print "Unable to match [{0}] element in list".format(x)
                return False
        else:
            retval = compate_generic_types(object1[x], object2[x])
            if retval is False:
                print "Unable to match [{0}] element in list".format(x)
                return False
    return retval
def deep_dict_compare(object1, object2):
    retval = True
    if len(object1) != len(object2):
        return False
    for k in object1.iterkeys():
        obj1 = object1[k]
        obj2 = object2[k]
        if isinstance(obj1, list) and isinstance(obj2, list):
            retval = deep_list_compare(obj1, obj2)
            if retval is False:
                print "Unable to match [{0}]".format(k)
                return False
        elif isinstance(obj1, dict) and isinstance(obj2, dict):
            retval = deep_dict_compare(obj1, obj2)
            if retval is False:
                print "Unable to match [{0}]".format(k)
                return False
        else:
            retval = compate_generic_types(obj1, obj2)
            if retval is False:
                print "Unable to match [{0}]".format(k)
                return False
    return retval

22

>>> x = {'a':1,'b':2,'c':3}
>>> x
{'a': 1, 'b': 2, 'c': 3}
>>> y = {'a':2,'b':4,'c':3}
>>> y
{'a': 2, 'b': 4, 'c': 3}
METHOD 1:
>>> common_item = x.items()&y.items() #using union,x.item() 
>>> common_item
{('c', 3)}
METHOD 2:
 >>> for i in x.items():
        if i in y.items():
           print('true')
        else:
           print('false')
false
false
true

23

In Python 3.6 kan het worden gedaan als: –

if (len(dict_1)==len(dict_2): 
  for i in dict_1.items():
        ret=bool(i in dict_2.items())

RET-variabele is waar als alle items van DICT_1 in het heden in DICT_2


24

Hier is mijn antwoord, gebruik een recursize manier:

def dict_equals(da, db):
    if not isinstance(da, dict) or not isinstance(db, dict):
        return False
    if len(da) != len(db):
        return False
    for da_key in da:
        if da_key not in db:
            return False
        if not isinstance(db[da_key], type(da[da_key])):
            return False
        if isinstance(da[da_key], dict):
            res = dict_equals(da[da_key], db[da_key])
            if res is False:
                return False
        elif da[da_key] != db[da_key]:
            return False
    return True
a = {1:{2:3, 'name': 'cc', "dd": {3:4, 21:"nm"}}}
b = {1:{2:3, 'name': 'cc', "dd": {3:4, 21:"nm"}}}
print dict_equals(a, b)

Hopelijk helpt dat!


Antwoord 25

import json
if json.dumps(dict1) == json.dumps(dict2):
    print("Equal")

Other episodes