2012-05-07 14 views
17

¿Cómo puedo comprobar si dos diccionarios son iguales sin tomar en cuenta algunas claves? Por ejemplo,Comparar diccionarios ignorando claves específicas

equal_dicts(
    {'foo':1, 'bar':2, 'x':55, 'y': 77 }, 
    {'foo':1, 'bar':2, 'x':66, 'z': 88 }, 
    ignore_keys=('x', 'y', 'z') 
) 

debe devolver True.

UPD: Estoy buscando una solución eficiente y rápida.

UPD2. Terminé con este código, que parece ser el más rápido:

def equal_dicts_1(a, b, ignore_keys): 
    ka = set(a).difference(ignore_keys) 
    kb = set(b).difference(ignore_keys) 
    return ka == kb and all(a[k] == b[k] for k in ka) 

Tiempos: https://gist.github.com/2651872

Respuesta

14
def equal_dicts(d1, d2, ignore_keys): 
    d1_filtered = dict((k, v) for k,v in d1.iteritems() if k not in ignore_keys) 
    d2_filtered = dict((k, v) for k,v in d2.iteritems() if k not in ignore_keys) 
    return d1_filtered == d2_filtered 

EDIT: Esto podría ser más rápido y más eficaz memoria:

def equal_dicts(d1, d2, ignore_keys): 
    ignored = set(ignore_keys) 
    for k1, v1 in d1.iteritems(): 
     if k1 not in ignored and (k1 not in d2 or d2[k1] != v1): 
      return False 
    for k2, v2 in d2.iteritems(): 
     if k2 not in ignored and k2 not in d1: 
      return False 
    return True 
+0

+1 (¡mejor que mi respuesta!) Además, si uno usa Python 3, puede usar una [comprensión de dict] (http://docs.python.org/py3k/tutorial/datastructures.html#dictionaries) (desplácese hacia abajo un poco) en lugar de la 'd expresión idiomática ict (). – huon

+0

Esta es una solución sencilla, pero en mi situación la eficiencia importa. – georg

+0

@ thg435 - ver mi respuesta actualizada. – eumiro

0

Muy muy crudamente, usted podría simplemente eliminar las claves ignoradas y comparar esos diccionarios:

def equal_dicts(d1, d2, ignore_keys=()): 
    d1_, d2_ = d1.copy(), d2.copy() 
    for k in ignore_keys: 
     try: 
      del d1_[k] 
     except KeyError: 
      pass 
     try: 
      del d2_[k] 
     except KeyError: 
      pass 

    return d1_ == d2_ 

(Tenga en cuenta que no necesitamos una copia en profundidad aquí, sólo tenemos que evitar modificar d1 y d2.)

+1

crudo de hecho)))) – georg

1
def compare_dict(d1, d2, ignore): 
    for k in d1: 
     if k in ignore: 
      continue 
     try: 
      if d1[k] != d2[k]: 
       return False 
     except KeyError: 
      return False 
    return True 

comentario de edición: Usted puede hacer algo como compare_dict(d1, d2, ignore) and compare_dict(d2, d1, ignore) o duplicar la for

def compare_dict(d1, d2, ignore): 
    ignore = set(ignore) 
    for k in d1: 
     if k in ignore: 
      continue 
     try: 
      if d1[k] != d2[k]: 
       return False 
     except KeyError: 
      return False 

    for k in d2: 
     if k in ignore: 
      continue 
     try: 
      if d1[k] != d2[k]: 
       return False 
     except KeyError: 
      return False 
    return True 

¡Lo que es más rápido y más limpio! Actualización: conjunto de molde (ignorar)

+1

Gracias, pero no creo que esto funcione cuando 'd2' tiene claves adicionales. – georg

8
{k: v for k,v in d1.iteritems() if k not in ignore_keys} == {k: v for k,v in d2.iteritems() if k not in ignore_keys} 
+0

Esto funcionará en Python 2.7 y 3. –

+0

Gracias, pero vea mi comentario a la respuesta de eumiro. Prefiero no construir dos costosas estructuras de memoria solo para compararlas. – georg

+0

luego puede escribir el bucle de forma manual, pero puede encontrar la comprensión más rápida de todos modos debido a la implementación C – wim

0

solución óptima para el caso de no hacer caso sólo una llave

return all(
    (x == y or (x[1] == y[1] == 'key to ignore')) for x, y in itertools.izip(
      d1.iteritems(), d2.iteritems())) 
+0

Cuidado: esto probablemente no funcionó correctamente en todos los casos en las versiones anteriores de Python (por ejemplo, tablas hash de diferentes tamaños, etc.) De manera análoga, la implementación análoga funciona en Python 3.6+ porque los métodos dict.items() etc. ahora devuelven los ítems en orden de inserción, no orden de hashtable. – intgr

0

en caso de que su diccionario contenía listas o otros diccionarios especializados:

def equal_dicts(d1, d2, ignore_keys, equal): 
    # print('got d1', d1) 
    # print('got d2', d2) 
    if isinstance(d1, str): 
     if not isinstance(d2, str): 
      return False 
     return d1 == d2 
    for k in d1: 
     if k in ignore_keys: 
      continue 
     if not isinstance(d1[k], dict) and not isinstance(d1[k], list) and d2.get(k) != d1[k]: 
      print(k) 
      equal = False 
     elif isinstance(d1[k], list): 
      if not isinstance(d2.get(k), list): 
       equal = False 
      if len(d1[k]) != len(d2[k]): 
       return False 
      if len(d1[k]) > 0 and isinstance(d1[k][0], dict): 
       if not isinstance(d2[k][0], dict): 
        return False 
       d1_sorted = sorted(d1[k], key=lambda item: item.get('created')) 
       d2_sorted = sorted(d2[k], key=lambda item: item.get('created')) 
       equal = all(equal_dicts(x, y, ignore_keys, equal) for x, y in zip(d1_sorted, d2_sorted)) and equal 
      else: 
       equal = all(equal_dicts(x, y, ignore_keys, equal) for x, y in zip(d1[k], d2[k])) and equal 
     elif isinstance(d1[k], dict): 
      if not isinstance(d2.get(k), dict): 
       equal = False 
      print(k) 
      equal = equal_dicts(d1[k], d2[k], ignore_keys, equal) and equal 
    return equal 
Cuestiones relacionadas