从字典列表中创建非矛盾项的字典

时间:2012-03-28 13:42:39

标签: python dictionary

这个问题的灵感来自this question。我想从字典列表中获取一个字典,该字典应该包含所有字典中的所有键/值对,这些字典只包含一次,或者所有字典都对相关值达成一致。示例(取自上述发布):

dicts = [dict(a=3, b=89, d=2), dict(a=3, b=89, c=99), dict(a=3, b=42, c=33)]
print dict_itersection(dicts)

应该产生

{'a': 3, 'd': 2}

我目前的实现如下:

import collections

def dict_intersection(dicts):
        c=collections.defaultdict(set)
        for d in dicts:
                for a, b in d.iteritems():
                        c[a].add(b)
        return {a: next(iter(b)) for a, b in c.iteritems() if len(b) == 1}

所以我的问题:这可以更优雅地完成吗?

边注:如果不修改基础字典(即不是next(iter(b))),b.pop()可以做得更好吗?

4 个答案:

答案 0 :(得分:4)

你的非常接近我能想到的优雅。我要做的唯一改变是用itertools.chain()'ed迭代器替换嵌套的for循环,如下所示:

import collections

def dict_intersection(dicts):
        c=collections.defaultdict(set)
        for k,v in itertools.chain(*[d.iteritems() for d in dicts]):
                c[k].add(v)
        return {a: next(iter(b)) for a, b in c.iteritems() if len(b) == 1}

编辑(1):下面的代码回答了一个稍微不同的问题 - 如何获得在至少两个输入词典中出现相同键和值的任何条目。

我在另一个问题的评论中回答:

dict(
    [k for k,count in
     collections.Counter(itertools.chain(*[d.iteritems() for d in dicts])).iteritems()
     if count > 1]
    )

这名义上是“单行”,但我已将其分散到多行(希望)使其更清晰。

它的工作方式是(从内部开始计算):

  • 使用itertools.chain()获取所有词典元素的迭代器。
  • 使用collections.Counter()计算每个key, value对出现在词典中的次数。
  • 使用列表推导来过滤至少两次Counter对的key, value
  • 将列表转换回dict。

答案 1 :(得分:4)

dicts = [dict(a=3, b=89, d=2), dict(a=3, b=89, c=99), dict(a=3, b=42, c=33)]

data = {}
for d in dicts:
    for k, v in d.iteritems():
        data.setdefault(k, set()).add(v)
out = dict((k, v.pop()) for k, v in data.iteritems() if len(v) == 1)

# out == {'a': 3, 'd': 2}

......或单行:

import itertools as it

dict((k, v.pop()[1]) for k,v in ((k, set(v)) for k, v in it.groupby(sorted(it.chain(*(d.iteritems() for d in dicts))), key=lambda x: x[0])) if len(v) == 1)

答案 2 :(得分:3)

到目前为止,所有解决方案都假设所有字典值都是可清除的。如果没有这个假设,代码不会变慢,只会变得更复杂,我会放弃它。这是适用于支持!=的所有值的版本:

def dict_intersection(dicts):
    result = {}
    conflicting = set()
    for d in dicts:
        for k, v in d.iteritems():
            if k not in conflicting and result.setdefault(k, v) != v:
                del result[k]
                conflicting.add(k)
    return result

集合conflicting将只包含字典键,它们始终可以清除。

答案 3 :(得分:1)

要获得交集:

dict(reduce(lambda x, y: x & y, map(set, map(lambda x: x.iteritems(), dicts))))

当然,这会丢掉唯一的值,所以我们需要得到补充:

dict(reduce(lambda x, y: x - y, map(set, map(lambda x: x.iteritems(), dicts))))

组合生成的词典为我们提供了结果集:

def dict_intersection(d):
    x = dict(reduce(lambda x, y: x & y, map(set, map(lambda x: x.iteritems(), dicts))))
    y = dict(reduce(lambda x, y: x - y, map(set, map(lambda x: x.iteritems(), dicts))))
    return dict(x.items() + y.items())

如果我的设置更强,我可以把它降到一个班轮,但今天似乎没有。