我想找到一种简单快捷的方法来查找python中给出N个词典的所有常见对(pair:value)。 ( 3.X最好)
问题
给定一组3 dicts
(但它可以是任何dict
,它仅用于示例)
n1 = {'a': 1, 'b': 2, 'c': 3}
n2 = {'a': 1, 'b': 4, 'c': 3, 'd': 4}
n3 = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
n1
,n2
和n3
的公共(键:值)结果
应该是:
({'a': 1, 'c': 3})
对于n2
和n3
,它应该是
({'a': 1, 'c': 3, 'd': 4})
我首先尝试使用强力算法来检查每个字典的每一对(键:值)
这是使用递归算法的实现
解决方案A
list_dict = [n1, n2, n3]
def finding_uniquness(ls):
def recursion(ls, result):
if not ls:
return result
result = {k: v for k, v in result.items() for k1, v1 in ls[0].items() if k == k1 and v == v1}
return recursion(ls[1:], result)
return recursion(ls[1:], ls[0])
finding_uniquness(list_dict)
# {'c': 3, 'a': 1}
但它不容易理解,而且复杂性很高
(我不确定如何计算复杂度;但是因为我们比较了所有dict
上的所有元素,复杂性应该是O(N²)?)
然后,我虽然关于Sets
。因为它可以自然地比较所有元素
解决方案B
import functools
list_dict = [n1, n2, n3]
set_list = [set(n.items()) for n in list_dict]
functools.reduce(lambda x, y: x & y, set_list)
# {('a', 1), ('c', 3)}
不幸的是,它比以前的解决方案好得多,当其中一个key
的值为list
时会抛出错误:
>>> n = {'a': [], 'b': 2, 'c': 3}
>>> set(n.items())
TypeError:不可用类型:'list'
我的问题是双重的:
TypeError
?当然,欢迎任何其他言论。
答案 0 :(得分:7)
更简单,更有效的方式:
>>> {k: v
for k, v in list_dict[0].items()
if all(k in d and d[k] == v
for d in list_dict[1:])}
{'c': 3, 'a': 1}
为list_dict[1:]
使用额外变量可能是有益的,否则all
的短路会有些浪费。或者,如果您之后不需要列表,则可以弹出“主”字典:
>>> {k: v
for k, v in list_dict.pop().items()
if all(k in d and d[k] == v
for d in list_dict)}
{'c': 3, 'a': 1}
或者使用get
,默认情况下不能出现在@ Jean-FrançoisFabre建议的字典中:
>>> marker = object()
>>> {k: v
for k, v in list_dict.pop().items()
if all(d.get(k, marker) == v
for d in list_dict)}
{'c': 3, 'a': 1}
答案 1 :(得分:3)
如果不可用值是一个问题,您始终可以使用.keys()
预先计算键的交集,然后仅比较与所有词典共有的键相关联的值:
import operator as op
from functools import reduce
common_keys = reduce(op.and_, (d.keys() for d in my_dicts))
common_items = {}
for key in common_keys:
value = my_dicts[0][key]
if all(d[key] == value for d in my_dicts):
common_items[key] = value
这应该比解决方案a快得多,比解决方案b慢,但适用于所有输入。
答案 2 :(得分:1)
包含电池的版本。
为了处理不可用的类型,我们使用酸洗;用莳萝或json或任何其他可预测的序列化来代替它。
import collections
import itertools
import pickle
def findCommonPairs(dicts):
all_pairs = itertools.chain(*[d.items() for d in dicts])
cnt = collections.Counter(map(pickle.dumps, all_pairs))
return [pickle.loads(pickled_pair)
for pickled_pair, count in cnt.items()
if count == len(dicts)]
>>> findCommonPairs([n1, n2, n3])
[('a', 1), ('c', 3)]
>>> findCommonPairs([{'a': [1,2], 'b': [2,3]}, {'a': [1,2]}])
[('a', [1, 2])]
请注意,序列化只是到目前为止。例如,为了正确地比较dicts,必须在序列化之前将这些dicts转换为(key,value)对并排序。任何相互引用的结构都可能存在问题(或不存在问题)。如果您关心这些问题,请用自定义可预测的序列化程序替换酸洗。