在python中找到N个字典的唯一(键:值)对

时间:2017-03-01 20:08:05

标签: python algorithm python-3.x dictionary

我想找到一种简单快捷的方法来查找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}

n1n2n3的公共(键:值)结果 应该是:

({'a': 1, 'c': 3})

对于n2n3,它应该是

({'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'

我的问题是双重的:

  • 有没有比解决方案A 更好的算法?
  • 或者有没有办法避免使用解决方案B TypeError

当然,欢迎任何其他言论。

3 个答案:

答案 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)对并排序。任何相互引用的结构都可能存在问题(或不存在问题)。如果您关心这些问题,请用自定义可预测的序列化程序替换酸洗。