keys - python dictionary of dictionaries
Calcule la diferencia en las claves contenidas en dos diccionarios de Python (21)
Supongamos que tengo dos diccionarios de Python: dictA
y dictB
. Necesito averiguar si hay claves que están presentes en dictB
pero no en dictA
. ¿Cuál es la forma más rápida de hacerlo?
¿Debo convertir las teclas del diccionario en un conjunto y luego seguir?
Interesado en conocer tus pensamientos ...
Gracias por tus respuestas.
Disculpas por no decir mi pregunta correctamente. Mi escenario es así: tengo un dictA
que puede ser el mismo que dictB
o pueden faltar algunas teclas en comparación con dictB
o, de lo contrario, el valor de algunas teclas puede ser diferente, que debe establecerse en el valor de la clave dictA
.
El problema es que el diccionario no tiene un estándar y puede tener valores que pueden ser dict de dict.
Decir
dictA={''key1'':a, ''key2'':b, ''key3'':{''key11'':cc, ''key12'':dd}, ''key4'':{''key111'':{....}}}
dictB={''key1'':a, ''key2:'':newb, ''key3'':{''key11'':cc, ''key12'':newdd, ''key13'':ee}.......
Por lo tanto, el valor ''key2'' debe restablecerse al nuevo valor y debe agregarse ''key13'' dentro del dict. El valor clave no tiene un formato fijo. Puede ser un valor simple o un dict o un dict de dict.
¿qué pasa con standart (comparar Objeto COMPLETO)
PyDev-> nuevo módulo PyDev-> módulo: unittest
import unittest
class Test(unittest.TestCase):
def testName(self):
obj1 = {1:1, 2:2}
obj2 = {1:1, 2:2}
self.maxDiff = None # sometimes is usefull
self.assertDictEqual(d1, d2)
if __name__ == "__main__":
#import sys;sys.argv = ['''', ''Test.testName'']
unittest.main()
@Maxx tiene una respuesta excelente, use las herramientas de prueba unitarias provistas por Python:
import unittest
class Test(unittest.TestCase):
def runTest(self):
pass
def testDict(self, d1, d2, maxDiff=None):
self.maxDiff = maxDiff
self.assertDictEqual(d1, d2)
Luego, en cualquier parte de tu código, puedes llamar:
try:
Test().testDict(dict1, dict2)
except Exception, e:
print e
La salida resultante se parece a la salida de diff
, imprimiendo bastante los diccionarios con +
o -
anteponiendo cada línea que es diferente.
A continuación, creé dos diccionarios. Necesito devolver la clave y valorar las diferencias entre ellos. Estoy atrapado aquí. No estoy seguro de cuál es la forma correcta. Necesito saber cómo obtener la diferencia de valor clave. Primero quiero comprobar si son iguales y si no deben imprimir las diferencias de valores clave. No quiero usar diff profundo No sé para comparar si son lo mismo?
num_list = [1,2]
val_list = [0,1]
dict1 = dict(zip(num_list,val_list))
print dict1
num_list2= [1,2]
val_list2 = [0,6]
dict2 = dict(zip(num_list2,val_list2))
print dict2
if dict1 == dict2
salida: actualmente {1: 0, 2: 1} {1: 0, 2: 6}
Aquí hay una solución para comparar profundamente 2 claves de diccionarios:
def compareDictKeys(dict1, dict2):
if type(dict1) != dict or type(dict2) != dict:
return False
keys1, keys2 = dict1.keys(), dict2.keys()
diff = set(keys1) - set(keys2) or set(keys2) - set(keys1)
if not diff:
for key in keys1:
if (type(dict1[key]) == dict or type(dict2[key]) == dict) and not compareDictKeys(dict1[key], dict2[key]):
diff = True
break
return not diff
Aquí hay una solución que puede comparar más de dos dictados:
def diff_dict(dicts, default=None):
diff_dict = {}
# add ''list()'' around ''d.keys()'' for python 3 compatibility
for k in set(sum([d.keys() for d in dicts], [])):
# we can just use "values = [d.get(k, default) ..." below if
# we don''t care that d1[k]=default and d2[k]=missing will
# be treated as equal
if any(k not in d for d in dicts):
diff_dict[k] = [d.get(k, default) for d in dicts]
else:
values = [d[k] for d in dicts]
if any(v != values[0] for v in values):
diff_dict[k] = values
return diff_dict
ejemplo de uso:
import matplotlib.pyplot as plt
diff_dict([plt.rcParams, plt.rcParamsDefault, plt.matplotlib.rcParamsOrig])
Basado en la respuesta de ghostdog74,
dicta = {"a":1,"d":2}
dictb = {"a":5,"d":2}
for value in dicta.values():
if not value in dictb.values():
print value
imprimirá difieren valor de dicta
Como escribió Alex Martelli, si simplemente quiere comprobar si alguna tecla en B no está en A, any(True for k in dictB if k not in dictA)
sería el camino a seguir.
Para encontrar las claves que faltan:
diff = set(dictB)-set(dictA) #sets
C:/Dokumente und Einstellungen/thc>python -m timeit -s "dictA =
dict(zip(range(1000),range
(1000))); dictB = dict(zip(range(0,2000,2),range(1000)))" "diff=set(dictB)-set(dictA)"
10000 loops, best of 3: 107 usec per loop
diff = [ k for k in dictB if k not in dictA ] #lc
C:/Dokumente und Einstellungen/thc>python -m timeit -s "dictA =
dict(zip(range(1000),range
(1000))); dictB = dict(zip(range(0,2000,2),range(1000)))" "diff=[ k for k in dictB if
k not in dictA ]"
10000 loops, best of 3: 95.9 usec per loop
Entonces esas dos soluciones son más o menos de la misma velocidad.
Como se menciona en otras respuestas, unittest produce una buena salida para comparar dicts, pero en este ejemplo no queremos tener que construir primero una prueba completa.
Al raspar la fuente de la prueba unitaria, parece que puede obtener una solución justa con solo esto:
import difflib
import pprint
def diff_dicts(a, b):
if a == b:
return ''''
return ''/n''.join(
difflib.ndiff(pprint.pformat(a, width=30).splitlines(),
pprint.pformat(b, width=30).splitlines())
)
asi que
dictA = dict(zip(range(7), map(ord, ''python'')))
dictB = {0: 112, 1: ''spam'', 2: [1,2,3], 3: 104, 4: 111}
print diff_dicts(dictA, dictB)
Resultados en:
{0: 112,
- 1: 121,
- 2: 116,
+ 1: ''spam'',
+ 2: [1, 2, 3],
3: 104,
- 4: 111,
? ^
+ 4: 111}
? ^
- 5: 110}
Dónde:
- ''-'' indica clave / valores en el primer pero no segundo dict
- ''+'' indica clave / valores en el segundo pero no en el primer dict
Al igual que en la prueba de unidad, la única advertencia es que la asignación final se puede considerar como una diferencia, debido a la coma / paréntesis final.
En caso de que quiera la diferencia recursivamente, he escrito un paquete para python: https://github.com/seperman/deepdiff
Instalación
Instalar desde PyPi:
pip install deepdiff
Ejemplo de uso
Importador
>>> from deepdiff import DeepDiff
>>> from pprint import pprint
>>> from __future__ import print_function # In case running on Python 2
El mismo objeto regresa vacío
>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = t1
>>> print(DeepDiff(t1, t2))
{}
El tipo de artículo ha cambiado
>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = {1:1, 2:"2", 3:3}
>>> pprint(DeepDiff(t1, t2), indent=2)
{ ''type_changes'': { ''root[2]'': { ''newtype'': <class ''str''>,
''newvalue'': ''2'',
''oldtype'': <class ''int''>,
''oldvalue'': 2}}}
El valor de un artículo ha cambiado
>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = {1:1, 2:4, 3:3}
>>> pprint(DeepDiff(t1, t2), indent=2)
{''values_changed'': {''root[2]'': {''newvalue'': 4, ''oldvalue'': 2}}}
Artículo agregado y / o eliminado
>>> t1 = {1:1, 2:2, 3:3, 4:4}
>>> t2 = {1:1, 2:4, 3:3, 5:5, 6:6}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff)
{''dic_item_added'': [''root[5]'', ''root[6]''],
''dic_item_removed'': [''root[4]''],
''values_changed'': {''root[2]'': {''newvalue'': 4, ''oldvalue'': 2}}}
Diferencia de cadena
>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world"}}
>>> t2 = {1:1, 2:4, 3:3, 4:{"a":"hello", "b":"world!"}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{ ''values_changed'': { ''root[2]'': {''newvalue'': 4, ''oldvalue'': 2},
"root[4][''b'']": { ''newvalue'': ''world!'',
''oldvalue'': ''world''}}}
Diferencia de cadena 2
>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world!/nGoodbye!/n1/n2/nEnd"}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world/n1/n2/nEnd"}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{ ''values_changed'': { "root[4][''b'']": { ''diff'': ''--- /n''
''+++ /n''
''@@ -1,5 +1,4 @@/n''
''-world!/n''
''-Goodbye!/n''
''+world/n''
'' 1/n''
'' 2/n''
'' End'',
''newvalue'': ''world/n1/n2/nEnd'',
''oldvalue'': ''world!/n''
''Goodbye!/n''
''1/n''
''2/n''
''End''}}}
>>>
>>> print (ddiff[''values_changed'']["root[4][''b'']"]["diff"])
---
+++
@@ -1,5 +1,4 @@
-world!
-Goodbye!
+world
1
2
End
Tipo de cambio
>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world/n/n/nEnd"}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{ ''type_changes'': { "root[4][''b'']": { ''newtype'': <class ''str''>,
''newvalue'': ''world/n/n/nEnd'',
''oldtype'': <class ''list''>,
''oldvalue'': [1, 2, 3]}}}
Lista de diferencia
>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3, 4]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{''iterable_item_removed'': {"root[4][''b''][2]": 3, "root[4][''b''][3]": 4}}
Lista de diferencia 2:
>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 3, 2, 3]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{ ''iterable_item_added'': {"root[4][''b''][3]": 3},
''values_changed'': { "root[4][''b''][1]": {''newvalue'': 3, ''oldvalue'': 2},
"root[4][''b''][2]": {''newvalue'': 2, ''oldvalue'': 3}}}
Listar la diferencia ignorando el orden o duplicar: (con los mismos diccionarios que arriba)
>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 3, 2, 3]}}
>>> ddiff = DeepDiff(t1, t2, ignore_order=True)
>>> print (ddiff)
{}
Lista que contiene diccionario:
>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, {1:1, 2:2}]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, {1:3}]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{ ''dic_item_removed'': ["root[4][''b''][2][2]"],
''values_changed'': {"root[4][''b''][2][1]": {''newvalue'': 3, ''oldvalue'': 1}}}
Conjuntos:
>>> t1 = {1, 2, 8}
>>> t2 = {1, 2, 3, 5}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (DeepDiff(t1, t2))
{''set_item_added'': [''root[3]'', ''root[5]''], ''set_item_removed'': [''root[8]'']}
Nombrado Tuples:
>>> from collections import namedtuple
>>> Point = namedtuple(''Point'', [''x'', ''y''])
>>> t1 = Point(x=11, y=22)
>>> t2 = Point(x=11, y=23)
>>> pprint (DeepDiff(t1, t2))
{''values_changed'': {''root.y'': {''newvalue'': 23, ''oldvalue'': 22}}}
Objetos personalizados:
>>> class ClassA(object):
... a = 1
... def __init__(self, b):
... self.b = b
...
>>> t1 = ClassA(1)
>>> t2 = ClassA(2)
>>>
>>> pprint(DeepDiff(t1, t2))
{''values_changed'': {''root.b'': {''newvalue'': 2, ''oldvalue'': 1}}}
Atributo de objeto agregado:
>>> t2.c = "new attribute"
>>> pprint(DeepDiff(t1, t2))
{''attribute_added'': [''root.c''],
''values_changed'': {''root.b'': {''newvalue'': 2, ''oldvalue'': 1}}}
Esta es una forma que funcionará, permite que las claves que evalúan False
, y todavía utiliza una expresión de generador para caerse temprano si es posible. Aunque no es excepcionalmente bonito.
any(map(lambda x: True, (k for k in b if k not in a)))
EDITAR:
THC4k publicó una respuesta a mi comentario en otra respuesta. Aquí hay una manera mejor y más bonita de hacer lo anterior:
any(True for k in b if k not in a)
No estoy seguro de cómo eso nunca pasó por mi mente ...
Esta es una vieja pregunta y pregunta un poco menos de lo que necesitaba, por lo que esta respuesta realmente resuelve más de lo que esta pregunta me pide. Las respuestas en esta pregunta me ayudaron a resolver lo siguiente:
- (preguntado) Registre las diferencias entre dos diccionarios
- Combina las diferencias desde el # 1 al diccionario base
- (preguntado) Fusionar las diferencias entre dos diccionarios (tratar el diccionario n. ° 2 como si fuera un diccionario de diferencias)
- Intenta detectar los movimientos de los artículos y los cambios
- (preguntado) Haz todo esto recursivamente
Todo esto combinado con JSON brinda un soporte de almacenamiento de configuración bastante poderoso.
La solución ( también en github ):
from collections import OrderedDict
from pprint import pprint
class izipDestinationMatching(object):
__slots__ = ("attr", "value", "index")
def __init__(self, attr, value, index):
self.attr, self.value, self.index = attr, value, index
def __repr__(self):
return "izip_destination_matching: found match by ''%s'' = ''%s'' @ %d" % (self.attr, self.value, self.index)
def izip_destination(a, b, attrs, addMarker=True):
"""
Returns zipped lists, but final size is equal to b with (if shorter) a padded with nulls
Additionally also tries to find item reallocations by searching child dicts (if they are dicts) for attribute, listed in attrs)
When addMarker == False (patching), final size will be the longer of a, b
"""
for idx, item in enumerate(b):
try:
attr = next((x for x in attrs if x in item), None) # See if the item has any of the ID attributes
match, matchIdx = next(((orgItm, idx) for idx, orgItm in enumerate(a) if attr in orgItm and orgItm[attr] == item[attr]), (None, None)) if attr else (None, None)
if match and matchIdx != idx and addMarker: item[izipDestinationMatching] = izipDestinationMatching(attr, item[attr], matchIdx)
except:
match = None
yield (match if match else a[idx] if len(a) > idx else None), item
if not addMarker and len(a) > len(b):
for item in a[len(b) - len(a):]:
yield item, item
def dictdiff(a, b, searchAttrs=[]):
"""
returns a dictionary which represents difference from a to b
the return dict is as short as possible:
equal items are removed
added / changed items are listed
removed items are listed with value=None
Also processes list values where the resulting list size will match that of b.
It can also search said list items (that are dicts) for identity values to detect changed positions.
In case such identity value is found, it is kept so that it can be re-found during the merge phase
@param a: original dict
@param b: new dict
@param searchAttrs: list of strings (keys to search for in sub-dicts)
@return: dict / list / whatever input is
"""
if not (isinstance(a, dict) and isinstance(b, dict)):
if isinstance(a, list) and isinstance(b, list):
return [dictdiff(v1, v2, searchAttrs) for v1, v2 in izip_destination(a, b, searchAttrs)]
return b
res = OrderedDict()
if izipDestinationMatching in b:
keepKey = b[izipDestinationMatching].attr
del b[izipDestinationMatching]
else:
keepKey = izipDestinationMatching
for key in sorted(set(a.keys() + b.keys())):
v1 = a.get(key, None)
v2 = b.get(key, None)
if keepKey == key or v1 != v2: res[key] = dictdiff(v1, v2, searchAttrs)
if len(res) <= 1: res = dict(res) # This is only here for pretty print (OrderedDict doesn''t pprint nicely)
return res
def dictmerge(a, b, searchAttrs=[]):
"""
Returns a dictionary which merges differences recorded in b to base dictionary a
Also processes list values where the resulting list size will match that of a
It can also search said list items (that are dicts) for identity values to detect changed positions
@param a: original dict
@param b: diff dict to patch into a
@param searchAttrs: list of strings (keys to search for in sub-dicts)
@return: dict / list / whatever input is
"""
if not (isinstance(a, dict) and isinstance(b, dict)):
if isinstance(a, list) and isinstance(b, list):
return [dictmerge(v1, v2, searchAttrs) for v1, v2 in izip_destination(a, b, searchAttrs, False)]
return b
res = OrderedDict()
for key in sorted(set(a.keys() + b.keys())):
v1 = a.get(key, None)
v2 = b.get(key, None)
#print "processing", key, v1, v2, key not in b, dictmerge(v1, v2)
if v2 is not None: res[key] = dictmerge(v1, v2, searchAttrs)
elif key not in b: res[key] = v1
if len(res) <= 1: res = dict(res) # This is only here for pretty print (OrderedDict doesn''t pprint nicely)
return res
Hay otra pregunta en sobre este argumento y tengo que admitir que hay una solución simple explicada: la biblioteca datadiff de python ayuda a imprimir la diferencia entre dos diccionarios.
Intenta esto para encontrar la intersección, las teclas que están en ambos dictionarie, si quieres las claves que no se encuentran en el segundo dictionarie, simplemente utiliza el no en ...
intersect = filter(lambda x, dictB=dictB.keys(): x in dictB, dictA.keys())
Mi receta de diferencia simétrica entre dos diccionarios:
def find_dict_diffs(dict1, dict2):
unequal_keys = []
unequal_keys.extend(set(dict1.keys()).symmetric_difference(set(dict2.keys())))
for k in dict1.keys():
if dict1.get(k, ''N/A'') != dict2.get(k, ''N/A''):
unequal_keys.append(k)
if unequal_keys:
print ''param'', ''dict1/t'', ''dict2''
for k in set(unequal_keys):
print str(k)+''/t''+dict1.get(k, ''N/A'')+''/t ''+dict2.get(k, ''N/A'')
else:
print ''Dicts are equal''
dict1 = {1:''a'', 2:''b'', 3:''c'', 4:''d'', 5:''e''}
dict2 = {1:''b'', 2:''a'', 3:''c'', 4:''d'', 6:''f''}
find_dict_diffs(dict1, dict2)
Y el resultado es:
param dict1 dict2
1 a b
2 b a
5 e N/A
6 N/A f
No estoy seguro de si todavía es relevante pero me encontré con este problema, mi situación solo necesitaba devolver un diccionario de los cambios para todos los diccionarios anidados, etc. No pude encontrar una buena solución pero acabé escribiendo una función simple para hacer esto Espero que esto ayude,
Puede usar operaciones de conjunto en las teclas:
diff = set(dictb.keys()) - set(dicta.keys())
Aquí hay una clase para encontrar todas las posibilidades: qué se agregó, qué se eliminó, qué pares clave-valor son iguales y qué pares clave-valor se cambian.
class DictDiffer(object):
"""
Calculate the difference between two dictionaries as:
(1) items added
(2) items removed
(3) keys same in both but changed values
(4) keys same in both and unchanged values
"""
def __init__(self, current_dict, past_dict):
self.current_dict, self.past_dict = current_dict, past_dict
self.set_current, self.set_past = set(current_dict.keys()), set(past_dict.keys())
self.intersect = self.set_current.intersection(self.set_past)
def added(self):
return self.set_current - self.intersect
def removed(self):
return self.set_past - self.intersect
def changed(self):
return set(o for o in self.intersect if self.past_dict[o] != self.current_dict[o])
def unchanged(self):
return set(o for o in self.intersect if self.past_dict[o] == self.current_dict[o])
Aquí hay algunos resultados de muestra:
>>> a = {''a'': 1, ''b'': 1, ''c'': 0}
>>> b = {''a'': 1, ''b'': 2, ''d'': 0}
>>> d = DictDiffer(b, a)
>>> print "Added:", d.added()
Added: set([''d''])
>>> print "Removed:", d.removed()
Removed: set([''c''])
>>> print "Changed:", d.changed()
Changed: set([''b''])
>>> print "Unchanged:", d.unchanged()
Unchanged: set([''a''])
Disponible como repositorio de github: https://github.com/hughdbrown/dictdiffer
Si desea una solución integrada para una comparación completa con estructuras dict arbitrarias, la respuesta de @ Maxx es un buen comienzo.
import unittest
test = unittest.TestCase()
test.assertEqual(dictA, dictB)
Si está en Python ≥ 2.7:
# update different values in dictB
# I would assume only dictA should be updated,
# but the question specifies otherwise
for k in dictA.viewkeys() & dictB.viewkeys():
if dictA[k] != dictB[k]:
dictB[k]= dictA[k]
# add missing keys to dictA
dictA.update( (k,dictB[k]) for k in dictB.viewkeys() - dictA.viewkeys() )
Si realmente quiere decir exactamente lo que dice (que solo tiene que averiguar SI "hay claves" en B y no en A, NO QUÉ SIGNIFICAR), la forma más rápida debería ser:
if any(True for k in dictB if k not in dictA): ...
Si realmente necesita saber QUÉ CLAVES, si las hay, están en B y no en A, y no solo "SI" existen tales claves, entonces las respuestas existentes son bastante apropiadas (pero sí sugiero más precisión en preguntas futuras si eso es de hecho, lo que quieres decir ;-).
no estoy seguro si es "rápido" o no, pero normalmente, uno puede hacer esto
dicta = {"a":1,"b":2,"c":3,"d":4}
dictb = {"a":1,"d":2}
for key in dicta.keys():
if not key in dictb:
print key
set(dictA.keys()).intersection(dictB.keys())