基于键的字典映射

3
给定一个字典列表。
input = [
  {'key': k1, 'value': v1},
  {'key': k1, 'value': v2},
  {'key': k2, 'value': v3}
]

什么是将它们映射到输出的最简单方法?
output == {k1: (v1, v2), k2: (v3)}

我并不特别关心值的顺序。我想到的最好方法是:

output = dict()
for i in input:
    temp = output.get(i['key'], [])
    temp.append(i['value'])
    output[i['key']] = temp

有没有使用字典推导式的简洁方法来完成这个操作?我假设同样的过程也适用于具有属性的对象列表。
2个回答

4
你可以使用 collections.defaultdict 来遍历字典的值,然后将它们附加到 defaultdict 中:
>>> from collections import defaultdict
>>> a = defaultdict(tuple)
>>> for d in input:
...     a[d['key']] += (d['value'],)
... 
>>> a
defaultdict(<type 'tuple'>, {'k2': ('v3',), 'k1': ('v1', 'v2')})

我认为你想要使用 d.items() 而不是 d.values() - avim
@avim 注意,列表中的每个字典都恰好有两个值。for循环迭代每个字典中的值对,而不是键/值对 - skrrgwasme
你的代码对于那个情况是有效的。字典是无序容器,因此,值的顺序不保证按照你的想法排序。 - avim
@avim 哦,我现在明白了。我完全同意,但是用 d.items() 替换 d.values() 并不能解决这个问题。这个答案需要进行重大更改才能处理它(应该这样做)。 - skrrgwasme
关于stackoverflow礼仪的问题。现在它已经被编辑过了,是第一个问题,并且非常直接了当,我应该切换我的勾选标记吗?个人而言,我可能会使用上面提到的列表解决方案,但基于主观标准(不关心列表,宁愿使用普通字典),但我在原始问题中没有提到这些。你有什么想法? - Kevin Hill
显示剩余2条评论

3
在字典推导中,任何单个键只能被访问或修改一次。因此,为了确保多个值与单个键配对,这些值需要事先分组。天真的分组解决方案的性能最好为二次方。实际上,我想不出比立方更好的一行代码;它是一个丑陋的野兽,甚至不值得发布。
因此,基于defaultdict的方法几乎总是最好的选择。
但是,如果您的数据被保证已排序,或者您愿意接受O(n log n)的性能,则可以使用itertools.groupby
>>> input
[{'value': 1, 'key': 'a'}, {'value': 2, 'key': 'a'}, {'value': 3, 'key': 'b'}]
>>> {k:tuple(d['value'] for d in v) for k, v in
...  itertools.groupby(input, key=lambda d: d['key'])}
{'a': (1, 2), 'b': (3,)}

为了摆脱不美观的 lambda,您可以使用 operator
>>> {k:tuple(d['value'] for d in v) for k, v in
...  itertools.groupby(input, key=operator.itemgetter('key'))}
{'a': (1, 2), 'b': (3,)}

或者,如果你必须先进行排序:

>>> {k:tuple(d['value'] for d in v) for k, v in itertools.groupby(
...  sorted(input, key=operator.itemgetter('key')),
...  key=operator.itemgetter('key'))}
{'a': (1, 2), 'b': (3,)}

这些解决方案都不太理想,它们看起来有点像滥用了理解语法,除了第二个方案可能是个例外。
作为从“collections”导入的替代方案,你可以使用“setdefault”——尽管这会产生列表而不是元组:
>>> output = {}
>>> for d in input:
...     output.setdefault(d['key'], []).append(d['value'])
... 
>>> output
{'a': [1, 2], 'b': [3]}

最后,考虑这种替代方案--我无法确定我的感受,但它避免了所有的导入和异国特色,并产生元组:

>>> output = {d['key']:() for d in input}
>>> for d in input:
...     output[d['key']] += (d['value'],)
... 
>>> output
{'a': (1, 2), 'b': (3,)}

必须喜欢选项!我不介意列表,所以我认为我最喜欢setdefault选项。对我来说似乎读起来最清晰。谢谢! - Kevin Hill

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接