[英]Python find if two dictionaries have the same partitioning of keys by value
最Python化的方法是执行以下操作:假设我有两个字典A
和B
现在,字典的常规python相等性将检查每个字典中的值和键是否相同,如果对于字典的每个元素都成立,则它们是否相等。 我想对此进行修改以使字典相等,如果对于A
具有相同值的所有键集,该集合中的每个元素在B
具有相同值,但不一定与A
具有相同值。
例:
A = {'A':1, 'B':4, 'C':1}
B = {'A':9, 'B':2, 'C':9}
这里A == B
本质上,此字典表示一组集合,我想在其上实现集合相等性。
我的尝试
def eq(a,b):
if not a.keys() == b.keys():
return False
for grouping in ({k for k in a.keys() if a[k] == v} for v in a.values()):
if not len(set(b[x] for x in grouping)) == 1:
return False
return True
我不太喜欢这种方法,因为它不会短路,因为必须消耗整个生成器才能将其转换为集合。 想法是将第一个集合划分为组,以使每个组中的每个元素都具有相同的值。 然后,我要确保对于每个分组,该分组中的元素的值在另一组中都是相同的。
编辑对不起,我无法更清楚地解释它,我将给出更多示例。 一种更简单的思考方式是:我可以将任何字典转换为一组集合,如下所示:
A = {'A':3, 'B':3, 'C':3, 'R':4, 'T':4}
A = {{'A', 'B', 'C'}, {'R', 'T'}}
B = {'A':[], 'B':[], 'C':[], 'R':"", 'T':""}
B = {{'A', 'B', 'C'}, {'R', 'T'}}
A == B
进行一些更改,我只能得到:
def eq(a,b):
if not a.keys() == b.keys():
return False
for x, y in zip(a.values(), b.values()):
if not sorted([key for key in a.keys() if a[key] == x]) == sorted([key for key in b.keys() if b[key] == y]):
return False
return True
但稍微干净一点的是:
def eq(a,b):
d1 = {}
d2 = {}
for (x, y), (i, j) in zip(a.items(), b.items()):
d1.setdefault(y, []).append(x)
d2.setdefault(j, []).append(i)
return [sorted(i) for i in d1.values()] == [sorted(i) for i in d2.values()]
或更短:
def eq(a,b):
d1 = {y: sorted([i for i in a.keys() if a[i] == y]) for x, y in a.items()}
d2 = {y: sorted([i for i in b.keys() if b[i] == y]) for x, y in b.items()}
return list(d1.values()) == list(d2.values())
一种基于@pault建议的方法是创建键值的字典,然后查看两个字典的值是否以相同的方式组合在一起。
我还对反向字典的值进行排序以照顾顺序,以及比较它们时的最终值列表
from collections import defaultdict
def eq(A, B):
rev_A = defaultdict(list)
rev_B = defaultdict(list)
#Create the reverse dictionary
for k, v in A.items():
#If v is a list, convert it to tuple to make a hashable key
if isinstance(v, list):
rev_A[tuple(v)].append(k)
else:
rev_A[v].append(k)
for k, v in B.items():
if isinstance(v, list):
rev_B[tuple(v)].append(k)
else:
rev_B[v].append(k)
#Sort the values of reverse dictionary
for k, v in rev_A.items():
rev_A[k] = sorted(v)
for k, v in rev_B.items():
rev_B[k] = sorted(v)
#See if the values of both dictionaries group in same fashion
return list(sorted(rev_A.values())) == list(sorted(rev_B.values()))
A = {'A':1, 'B':4, 'C':1}
B = {'A':9, 'B':2, 'C':9}
print(eq(A,B))
A = {'A':3, 'B':3, 'C':3, 'R':4, 'T':4}
B = {'C':8, 'R':6, 'T':6, 'A':8, 'B':8}
print(eq(A,B))
A = {'A':3, 'B':3, 'C':3, 'R':4, 'T':4}
B = {'A':[], 'B':[], 'C':[], 'R':"", 'T':""}
print(eq(A,B))
输出将是
True
True
True
编辑:修复了@pault指出的问题。 尽管由于b
值不可散列,所以该特定输入现在会引发错误...
由于OP提到他们的原始方法不会短路,因此我将尝试给出一种可行的方法。 这种方法确实要求a
和b
的值是可哈希的。
我没有对此进行概要分析。 无论如何,这可能取决于输入的性质。 具体来说,如果可以对a
或b
值进行哈希处理,但是效率很低,那么这种方法当然会受到影响。
另一个想法:如果两个字典相等(在此定义下)或接近,那么此实现将需要比较python循环中的所有元素,这可能比其他实现慢。 但是,如果它们可能大相径庭,允许短路完成其工作,则此方法可能会显示出优势。
编辑:添加了参数encoding
来强制哈希某些对象。 当然,这取决于所使用的编码会产生一些副作用,例如[]
和()
被视为相等,而具有不同顺序的相等字典被视为不相等。
def eq(a, b, encoding = None):
if len(a) != len(b): return False
mapping = {}
value_set = set()
for k, v_a in a.items():
v_b = b.get(k)
if v_b is None: return False
if encoding: v_a, v_b = encoding(v_a), encoding(v_b)
if v_a in mapping:
if mapping[v_a] != v_b: return False
elif v_b in value_set: return False
else:
mapping[v_a] = v_b
value_set.add(v_b)
return True
用法:
import json
A = {'A':3, 'B':3, 'C':3, 'R':4, 'T':4}
B = {'A':[], 'B':[], 'C':[], 'R':"", 'T':""}
print(eq(A, B, encoding = json.dumps))
如果值不可散列,则其他答案将中断。 另一种方法是根据值对键进行分组,并检查两个字典的组是否相等。
一种方法是使用itertools.groupby
对密钥进行分组,但这将要求首先对项目进行排序。 但是, python 3不支持对异构列表进行排序 ,因此我们将不得不使用“ 如何在Python 3.x中获得类似2.x的排序行为”中的答案之一。 。
我选择@Fred的答案是因为我们不在乎排序顺序,并且它是最简单的编码。
from itertools import groupby
from operator import itemgetter
from numbers import Real
from decimal import Decimal
# from https://stackoverflow.com/a/26663384/5858851
def motley(value):
numeric = Real, Decimal
if isinstance(value, numeric):
typeinfo = numeric
else:
typeinfo = type(value)
try:
x = value < value
except TypeError:
value = repr(value)
return repr(typeinfo), value
def eq(A, B):
def get_key_groups(X):
return set(
tuple(map(itemgetter(0), g))
for i, g in groupby(
sorted(X.items(), key=lambda x: motley(x[1])),
key=itemgetter(1)
)
)
return get_key_groups(A) == get_key_groups(B)
一些测试:
A = {'A':1, 'B':4, 'C':1}
B = {'A':9, 'B':2, 'C':9}
eq(A, B)
#True
A = {'A':3, 'B':3, 'C':3, 'R':4, 'T':4}
B = {'A':[], 'B':[], 'C':[], 'R':"", 'T':""}
eq(A, B)
#True
A = {'A':3, 'B':2, 'C':3, 'R':4, 'T':4}
B = {'A':[], 'B':[], 'C':[], 'R':"", 'T':""}
eq(A, B)
#False
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.