基于Python,根据公共值合并/连接字典列表。

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字典。

非常感谢您的时间。


1
你确定那些是元组吗?{} 的语法是用于字典的... - thegrinner
1
这些不是元组,为什么不把你现在的代码展示给我们看看? - Marcin
谢谢。我已经编辑了问题,将元组替换为字典。 - edkay
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']

*有一些不同之处,但对于这个问题来说并不重要


在这里调用operator.itemgetter()可能是一个不错的选择。 - Gareth Latty
这是一个很好的解决方案,但值得注意的是,如果对于同一user_id,有多行包含相同的键值,则此解决方案将覆盖除最后一个值以外的所有结果。 对于这个问题可能没什么问题,但如果需要考虑这种情况,这可能会成为一个棘手的问题。 - Silas Ray
1
@sr2222 -- 你说得对,它确实会这样做,但如果这是一个问题,那么这不是一个明确定义的问题(OP从未说明应该如何处理) :) - mgilson
@sr2222 好主意。幸运的是,在这种情况下,由于使用的数据库查询,不会有任何重复的"user__id"值键。 - edkay
排序、分组和itemgetter对于某些字典来说似乎都是不必要的开销。 - Marcin
显示剩余6条评论

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的技巧在于它省去了为新条目创建字典的繁琐操作。

不需要对这些输入进行分组或排序。字典会处理所有这些内容。

一个真正强大的解决方案应该捕获潜在的键错误,以防输入没有'user__id'键,或者使用默认值来收集所有没有此类键的字典。


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