12

我有两个字典列表(作为 Django 查询集返回)。每个字典都有一个 ID 值。我想根据 ID 值将两者合并到一个字典列表中。

例如:

list_a = [{'user__name': u'Joe', 'user__id': 1},
          {'user__name': u'Bob', 'user__id': 3}]
list_b = [{'hours_worked': 25, 'user__id': 3},
          {'hours_worked': 40, 'user__id': 1}]

我想要一个函数来产生:

list_c = [{'user__name': u'Joe', 'user__id': 1, 'hours_worked': 40},
          {'user__name': u'Bob', 'user__id': 3, 'hours_worked': 25}]

需要注意的其他要点:

  • 列表中的 ID 的顺序可能不同(与上面的示例一样)。
  • 列表可能具有相同数量的元素,但如果不是,我想考虑该选项,但保留 list_a 中的所有值(本质上list_a OUTER JOIN list_b USING user__id)。
  • 我尝试在 SQL 中执行此操作,但这是不可能的,因为某些值是基于某些排除项的聚合。
  • user__id由于使用的数据库查询,可以安全地假设每个列表中最多只有一个具有相同的字典。

非常感谢您的时间。

4

2 回答 2

19

我会用来itertools.groupby对元素进行分组:

lst = sorted(itertools.chain(list_a,list_b), key=lambda x:x['user__id'])
list_c = []
for k,v in itertools.groupby(lst, key=lambda x:x['user__id']):
    d = {}
    for dct in v:
        d.update(dct)
    list_c.append(d)
    #could also do:
    #list_c.append( dict(itertools.chain.from_iterable(dct.items() for dct in v)) )
    #although that might be a little harder to read.

如果您对lambda函数有反感,您可以随时使用operator.itemgetter('user__id')。(它可能也稍微更有效率)

为了稍微揭开 lambda/itemgetter 的神秘面纱,请注意:

def foo(x):
    return x['user__id']

与以下任何一项相同*:

foo = operator.itemgetter('user__id')
foo = lambda x: x['user__id']

*有一些差异,但它们对于这个问题并不重要

于 2012-12-20T15:10:36.813 回答
6
from collections import defaultdict
from itertools import chain

list_a = [{'user__name': u'Joe', 'user__id': 1},
      {'user__name': u'Bob', 'user__id': 3}]
list_b = [{'hours_worked': 25, 'user__id': 3},
      {'hours_worked': 40, 'user__id': 1}]

collector = defaultdict(dict)

for collectible in chain(list_a, list_b):
    collector[collectible['user__id']].update(collectible.iteritems())

list_c = list(collector.itervalues())

如您所见,这只是使用另一个字典来合并现有的字典。defaultdict 的诀窍在于它消除了为新条目创建字典的苦差事。

无需对这些输入进行分组或排序。dict 负责所有这些。

如果输入没有“user__id”键,则真正的防弹解决方案会捕获潜在的键错误,或者使用默认值来收集没有这样键的所有字典。

于 2012-12-20T15:43:38.210 回答