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 x
dezelfde 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 tuple
wordt 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 deepdiff
heeft 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 (SHA
of 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 unittest
in uw productiecode. Mijn gedachte is de bron in Pyunit kan opnieuw worden gereageerd om in productie te lopen. Het gebruikt pprint
die 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")