Python:查找包含列表的两个字典之间的差异

13

我有两个字典,其结构如下:

a = {'joe': [24,32,422], 'bob': [1,42,32,24], 'jack':[0,3,222]}
b = {'joe': [24], 'bob': [1,42,32]}

我想获取这两个字典之间的差异,以此例而言结果应该是:

{'joe': [32,422], 'bob': [24], 'jack':[0,3,222]}

我知道可以用混乱的循环来做到这一点,但我想知道如何以简洁、Pythonic的方式实现?

我尝试过:a.items() - b.items()

但是我收到了以下错误:unsupported operand type(s) for -: 'dict_values' and 'dict_values'

谢谢你的帮助。


1
我想指出,使用被转换回列表的集合作为答案会破坏列表的顺序(如果顺序没有针对特定示例更改,则这是巧合)。由于您的规格说明没有说列表的顺序不重要,因此我认为应该注意到这一点。 - timgeb
3个回答

12

假设您的列表中没有重复项,则可以使用set,但无法使用列表进行所需操作:

>>> a = {'joe': [24,32,422], 'bob': [1,42,32,24], 'jack':[0,3,222]}
>>> b = {'joe': [24], 'bob': [1,42,32]}
>>> {key: list(set(a[key])- set(b.get(key,[]))) for key in a}
{'joe': [32, 422], 'bob': [24], 'jack': [0, 3, 222]}

注意两点:

  • 当我将集合设置为值时,我会将其转换回列表
  • 我使用 b.get 而不是 b[key] 来处理在b中不存在但在a中存在的键的情况

编辑 - 使用 for 循环:

我意识到推导可能不是那么易于理解,因此这是使用 for 循环的等效代码:

>>> c = {}
>>> for key in a:
    c[key] = list(set(a[key]) - set(b.get(key,[])))


>>> c
{'joe': [32, 422], 'bob': [24], 'jack': [0, 3, 222]}

编辑 - 删除第二个集合:

正如Padraic Cunningham在评论中提到的那样(他常常这样做,上帝保佑他的灵魂),你可以利用set.difference来避免显式地将第二个列表转换为一个集合:

>>> c = {}
>>> for key in a:
    c[key] = list(set(a[key]).difference(b.get(key,[])))


>>> c
{'joe': [32, 422], 'bob': [24], 'jack': [0, 3, 222]}

或者使用列表推导式:

>>> {key: list(set(a[key]).difference(b.get(key,[]))) for key in a}
{'joe': [32, 422], 'bob': [24], 'jack': [0, 3, 222]}

或者,如果你想将set.difference视为类方法而不是实例方法:

>>> {key: list(set.difference(set(a[key]),b.get(key,[]))) for key in a}
{'joe': [32, 422], 'bob': [24], 'jack': [0, 3, 222]}

虽然我觉得这有点笨拙,而且我并不是很喜欢它。


太好了!非常棒的答案,再次感谢您的解释。 - Ryan
你不需要调用两次set,你可以使用set.difference - Padraic Cunningham
@PadraicCunningham,您的意思是什么?set.difference不需要集合作为参数吗? - R Nar
@RNar,第二个参数可以是任何可迭代对象,调用set意味着你首先创建一个集合,然后检查每个值,使用.difference不会创建第二个集合。list(set(a[key]).difference(b.get(key,[])) - Padraic Cunningham
1
@PadraicCunningham 我刚刚测试了一下,之前不知道这个!谢谢你,我会进行编辑。 - R Nar

7

您需要使用集合(Sets):

diff = {}
for key in a:
    diff[key] = list(set(a[key]) - set(b.get(key, [])))
print diff

3
另一种方法是使用内置方法 filter
>>> a = {'joe': [24,32,422], 'bob': [1,42,32,24], 'jack':[0,3,222]}
>>> b = {'joe': [24], 'bob': [1,42,32]}
>>> {key:filter(lambda s: s not in b.get(key,[]), a[key]) for key in a}
{'bob': [24], 'joe': [32, 422], 'jack': [0, 3, 222]}

根据Padraic Cunningham的评论:
在Python 3中,filter返回一个generator,因此,您需要将其转换为列表,方法如下: {key:list(filter(lambda s: s not in b.get(key,[]), a[key])) for key in a}

1
使用Python3的任何人都需要 list(filter... - Padraic Cunningham
是的...但是OP提到它是2.7版本。 - Iron Fist
2
是的,但其他人可能会看到答案 ;) - Padraic Cunningham
1
感谢 @PadraicCunningham .. :) - Iron Fist

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