Pythonic方式减少"重复"字典中的元组

时间:2018-04-18 18:56:00

标签: python

我有一个元组字典,例如:

my_dict = {('A','B'): 3, ('A','C'): 4, ('B','A'): 5}

目标是组合键(无论排序如何)并添加各自的值,以使结果看起来像

my_dict = {('A','B'): 8, ('A','C'): 4}

我非常确定我可以使用以下内容执行此操作:

new_dict = {}
items = my_dict.copy().items()
for k, _ in items:
    to_add = my_dict.pop(k)
    for key, val in my_dict.items():
        if set(k) == set(key):
            new_dict[key] = val + to_add
    if (k not in new_dict) and ((k[1],k[0]) not in new_dict):
        new_dict[k] = to_add

但是,我对此解决方案并不满意。我创建了另一个字典,而不是维护原始字典,我有嵌套循环(虽然我不认为它非常O(N ** 2),因为第二个循环的长度总是在减小),而我有一种感觉,那就是更优雅的解决方案。

是否有更多的pythonic或更优雅的方式来完成这项任务?

编辑:

为清楚起见,这里有几个条件 - 所有元组都有2个元素,但元素不能保证可比,就像我们可能有('A', None)一样。不存在任何重复的元组,如('A', 'A'),元组的最终顺序并不重要。这意味着结果

my_dict = {('A','B'): 8, ('A','C'): 4}

不比

更好或更差
my_dict = {('B','A'): 8, ('A','C'): 4}

2 个答案:

答案 0 :(得分:3)

如果您不关心元组中的顺序或重复(即,如果您的代码将('B', 'A')转换为('A', 'A'),将('A',)转换为lambda x: type(x).__name__, x,那么&#39 ;很好),你可以使用frozensets而不是元组。

如果您关心重复但不关心顺序,排序的元组将起作用。 (虽然如果你的元组元素不具有可比性,你需要提出一个比较键 - 可能只是Counter一个Python 2,但可能更复杂。)

无论哪种方式,而不是建立一个字典,然后建立另一个总结重复的字典,只需在第一时间建立一个import collections c = collections.Counter() for key, value in <wherever they come from>: c[frozenset(key)] += value import collections c = collections.Counter() for key, value in <wherever they come from>: c[tuple(sorted(key))] += value

c = {}
for key, value in <wherever they come from>:
    skey = frozenset(key)
    if skey not in c:
        c[skey] = [key, 0]
    c[skey][1] += value

如果你需要保留但忽略顺序(同样的方式,例如,某些文件系统保留但忽略了大小写),你需要做更多的工作。 (您还需要决定是否要保留匹配的一组键中的第一个或最后一个。)一个选项是使用&#34;键转换字典&#34;包装dict,使用转换后的键作为底层键,将原始键作为值中的额外值。没有包装器,它看起来像这样:

redux-thunk

答案 1 :(得分:2)

要获得更加pythonic的解决方案,请使用collections.defaultdictcollections.Counter

import collections

new_dict = collections.defaultdict(int)
# alternatively: new_dict = collections.Counter()

for key, value in my_dict.items():
    # converting the tuples to frozensets removes the order and makes
    # them hashable
    key = frozenset(key)
    new_dict[key] += value

# turn the defaultdict with frozensets back into a normal dict with tuples
new_dict = {tuple(key): value for key, value in new_dict.items()}

结果:

{('A', 'B'): 8, ('A', 'C'): 4}

请记住,只有在元组中的值是唯一的时,这才有效。如果dict中有('A', 'A')这样的元组,则在其上调用frozenset会将其折叠为{'A'}并产生错误的输出。如果这是一个问题,您可以替换

key = frozenset(key)

key = tuple(sorted(key))

使其正常工作。