recorrer - diccionario python ejemplo
Comparando dos diccionarios en Python (16)
Código
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
Prueba
>>> a = {
''number'': 1,
''list'': [''one'', ''two'']
}
>>> b = {
''list'': [''two'', ''one''],
''number'': 1
}
>>> equal(a, b)
True
Tengo dos diccionarios, pero para simplificar, tomaré estos dos:
>>> x = dict(a=1, b=2)
>>> y = dict(a=2, b=2)
Ahora, quiero comparar si cada key, value
par de key, value
en x
tiene el mismo valor correspondiente en y
. Entonces escribí esto:
>>> 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
Y funciona desde que se devuelve una tuple
y luego se compara por igualdad.
Mis preguntas:
¿Es esto correcto? ¿Hay una mejor manera de hacer esto? Mejor no en velocidad, estoy hablando de elegancia de código.
ACTUALIZACIÓN: Olvidé mencionar que tengo que verificar cuántos pares key, value
son iguales.
En PyUnit hay un método que compara bellamente los diccionarios. Lo probé usando los dos diccionarios siguientes y hace exactamente lo que estás buscando.
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))
No recomiendo importar unittest
en tu código de producción. Mi pensamiento es que la fuente en PyUnit podría ser rediseñada para ejecutarse en producción. Utiliza pprint
que "imprime bastante" los diccionarios. Parece bastante fácil adaptar este código para que esté "listo para producción".
En Python 3.6, se puede hacer como: -
if (len(dict_1)==len(dict_2):
for i in dict_1.items():
ret=bool(i in dict_2.items())
La variable ret será verdadera si todos los elementos de dict_1 están presentes en dict_2
La función es buena IMO, clara e intuitiva. Pero solo para darle (otra) respuesta, aquí está mi respuesta:
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'')
Puede ser útil para usted o para cualquier otra persona ..
La respuesta de @mouad es agradable si se supone que ambos diccionarios solo contienen valores simples. Sin embargo, si tiene diccionarios que contienen diccionarios, obtendrá una excepción ya que los diccionarios no son aptos para el uso.
Por la parte superior de mi cabeza, algo como esto podría funcionar:
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(dict2.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
Tenga en cuenta que todavía no estoy contento con la línea:
dicts_are_equal = dicts_are_equal and (dict1[key] == dict2[key])
como los valores de estos podrían ser objetos. Sería bueno poder verificar si los dos objetos estaban implementando una interfaz comparable.
Lo que quieres hacer es simplemente x==y
Lo que hagas no es una buena idea, porque se supone que los elementos en un diccionario no tienen ningún orden. Usted podría estar comparando [(''a'',1),(''b'',1)]
con [(''b'',1), (''a'',1)]
(mismos diccionarios, diferente orden).
Por ejemplo, mira esto:
>>> 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))]
La diferencia es solo un elemento, pero su algoritmo verá que todos los elementos son diferentes
Otra posibilidad más, hasta la última nota del OP, es comparar los hash ( SHA
o MD
) de los dicts arrojados como JSON. La forma en que se construyen los valores hash garantiza que, si son iguales, las cadenas fuente son iguales también. Esto es muy rápido y matemáticamente sano.
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))
Para probar si dos dicts son iguales en claves y valores:
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)
Si desea devolver los valores que difieren, escríbalo de manera diferente:
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]}
Deberías llamarlo dos veces, es decir,
dict1_minus_d2(d1,d2).extend(dict1_minus_d2(d2,d1))
Para verificar si dos diccionarios tienen el mismo contenido, use:
dic1 == dic2
De los documentos de Python :
Para ilustrar, los siguientes ejemplos devuelven un diccionario igual a {"one": 1, "two": 2, "three": 3}
:
>>> a = dict(one=1, two=2, three=3)
>>> b = {''one'': 1, ''two'': 2, ''three'': 3}
>>> c = dict(zip([''one'', ''two'', ''three''], [1, 2, 3]))
>>> d = dict([(''two'', 2), (''one'', 1), (''three'', 3)])
>>> e = dict({''three'': 3, ''one'': 1, ''two'': 2})
>>> a == b == c == d == e
True
Si quieres saber cuántos valores coinciden en ambos diccionarios, deberías haber dicho eso :)
Tal vez algo como esto:
shared_items = set(x.items()) & set(y.items())
print len(shared_items)
Solo usa:
assert cmp(dict1, dict2) == 0
Soy nuevo en Python pero terminé haciendo algo similar a @mouad
unmatched_item = set(dict_1.items()) ^ set(dict_2.items())
len(unmatched_item) # should be 0
El operador XOR ( ^
) debería eliminar todos los elementos del dict cuando son iguales en ambos dicts.
ver objetos de vista de diccionario: https://docs.python.org/2/library/stdtypes.html#dict
De esta forma, puede restar dictView2 de dictView1 y devolverá un conjunto de pares clave / valor que son diferentes en 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'')])
Puede intersecar, unir, diferenciar (se muestra arriba), diferencia simétrica estos objetos de vista de diccionario.
¿Mejor? ¿Más rápido? - no estoy seguro, pero es parte de la biblioteca estándar, lo que lo convierte en una gran ventaja para la portabilidad
>>> 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''
Aquí hay otra opción:
>>> id(hash_1)
140640738806240
>>> id(hash_2)
140640738994848
Entonces, como ves, las dos identificaciones son diferentes. Pero los operadores de comparación ricos parecen hacer el truco:
>>> 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.
>>>
def dict_compare(d1, d2):
d1_keys = set(d1.keys())
d2_keys = set(d2.keys())
intersect_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 intersect_keys if d1[o] != d2[o]}
same = set(o for o in intersect_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)
import json
if json.dumps(dict1) == json.dumps(dict2):
print("Equal")