根据键映射dicts

时间:2015-03-12 19:43:08

标签: python dictionary

给定list dict s

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

有什么用dict理解这样做的吗?我假设同样的过程也适用于具有属性的对象列表。

2 个答案:

答案 0 :(得分: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')})

答案 1 :(得分: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,)}