2012-05-07 42 views
17

Jak mogę sprawdzić, czy dwa słowniki są jednakowe, biorąc pod uwagę niektóre klucze. Na przykład:Porównaj słowniki ignorujące określone klucze

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

powinien zwrócić wartość True.

UPD: Szukam wydajnego, szybkiego rozwiązania.

UPD2. Skończyło się tym kodem, który wydaje się być najszybszym:

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) 

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

Odpowiedz

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: To może być szybciej i więcej pamięci efektywne:

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 (lepiej niż moja odpowiedź!) Ponadto, jeśli zdarzy się, że używasz Pythona 3, możesz użyć funkcji [Dict Comprehension] (http://docs.python.org/py3k/tutorial/datastructures.html#dictionaries) (przewiń w dół trochę) zamiast "d" ict () 'idiom. – huon

+0

To proste rozwiązanie, ale w mojej sytuacji liczy się wydajność. – georg

+0

@ thg435 - zobacz moją zaktualizowaną odpowiedź. – eumiro

0

Bardzo ostrożnie, very, możesz po prostu usunąć wszystkie ignorowane klucze i porównać te słowniki:

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_ 

(Zauważ, że nie musimy głęboką kopię tutaj, po prostu trzeba unikać modyfikowania d1 i d2.)

+1

crude crude)))) – 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 

Komentarz edit: Można zrobić coś jak compare_dict(d1, d2, ignore) and compare_dict(d2, d1, ignore) lub powielać 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 

Cokolwiek jest szybsze i czystsze! Aktualizacja: zestaw obsada (zignorować)

+1

Dzięki, ale nie sądzę, że to zadziała, gdy 'd2' ma dodatkowe klucze. – 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

To będzie działać w Pythonie 2.7 i 3. –

+0

Dzięki, ale zobacz mój komentarz do odpowiedzi eumiro. Wolę nie budować dwóch drogich struktur pamięci tylko po to, żeby je porównać. – georg

+0

następnie można napisać pętlę ręcznie, ale może i tak być szybciej zrozumiane ze względu na implementację C – wim

0

optymalne rozwiązanie dla przypadku ignorowania tylko jeden klucz

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

Uwaga: to prawdopodobnie nie działało poprawnie we wszystkich przypadkach we wcześniejszych wersjach Pythona (np. Tabele mieszania o różnych rozmiarach itp.), Ale analogiczna implementacja na pewno działa w Pythonie 3.6+, ponieważ metody dict.items() itd. zwracają teraz elementy w kolejności wstawiania, a nie w kolejności tablicowej. – intgr

0

w przypadku słownika zawierała list lub inne słowniki:

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 
Powiązane problemy