2012-05-07 89 views
17

如何測試兩個字典是否相等,同時考慮一些密鑰。例如,比較字典忽略特定鍵

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

應返回true。

UPD:我正在尋找一個高效,快速的解決方案。

UPD2。我結束了這段代碼,這似乎是最快的:

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) 

時序:https://gist.github.com/2651872

回答

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 

編輯:這可能是更快,更多的內存效率:

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 (比我的回答好!)另外,如果碰巧使用Python 3,你可以使用[dict comprehension](http://docs.python.org/py3k/tutorial/datastructures.html#dictionaries)(向下滾動一點)代替d ict()成語。 – huon

+0

這是一個直接的解決方案,但在我的情況下效率很重要。 – georg

+0

@ thg435 - 查看我更新的答案。 – eumiro

0

非常非常粗略地說,您可以刪除任何被忽略的鍵並比較這些字典:

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_ 

(請注意,我們並不需要在這裏深拷貝,我們只需要避免修改d1d2。)

+1

確實)))) – 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 

註釋編輯:你可以不喜歡compare_dict(d1, d2, ignore) and compare_dict(d2, d1, ignore)或複製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 

無論更快更清潔! 更新:投集(忽略)

+1

謝謝,但我不認爲這將工作時'd2'有額外的鍵。 – 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

這將在Python 2中工作。7和3. –

+0

謝謝,但看到我對eumiro的回答的評論。我寧願不建立兩個昂貴的內存結構來比較它們。 – georg

+0

這時你可以手動編寫循環,但是你可能會發現更快地理解反正因爲C語言實現 – wim

0

只忽視一個關鍵的情況下,最優解

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

請注意:這可能沒有正確地在所有情況下在早期版本的Python工作(如不同大小的哈希表等),但一類似的實現肯定會在Python 3.6+中起作用,因爲dict.items()等方法現在以插入順序返回項目,而不是哈希表順序。 – intgr

0

的情況下,你的字典包含表或其他字典:

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