在Python中比较由唯一键组成的字典列表

47
我有两个列表,它们都包含相同数量的字典。每个字典都有一个唯一的键。第一个列表中的每个字典在第二个列表中都有一个匹配项,也就是说,另一个列表中存在一个具有唯一键的字典。但这两个字典的其他元素可能不同。例如:
list_1 = [
            {
                'unique_id': '001',
                'key1': 'AAA',
                'key2': 'BBB',
                'key3': 'EEE'
             },
             {
                'unique_id': '002',
                'key1': 'AAA',
                'key2': 'CCC',
                'key3': 'FFF'
             }
         ]

 list_2 = [
             {
                'unique_id': '001',
                'key1': 'AAA',
                'key2': 'DDD',
                'key3': 'EEE'
             },
             {
                'unique_id': '002',
                'key1': 'AAA',
                'key2': 'CCC',
                'key3': 'FFF'
             }
         ]
我想比较两个相对应的字典中的所有元素。如果任何元素不相等,我想打印这些不相等的元素。请帮忙吗?
8个回答

67
假设字典与您的示例输入一样对齐,您可以使用zip()函数获取一组相关的字典对,然后可以使用any()来检查是否存在差异。
>>> list_1 = [{'unique_id':'001', 'key1':'AAA', 'key2':'BBB', 'key3':'EEE'}, 
              {'unique_id':'002', 'key1':'AAA', 'key2':'CCC', 'key3':'FFF'}]
>>> list_2 = [{'unique_id':'001', 'key1':'AAA', 'key2':'DDD', 'key3':'EEE'},
              {'unique_id':'002', 'key1':'AAA', 'key2':'CCC', 'key3':'FFF'}]
>>> pairs = zip(list_1, list_2)
>>> any(x != y for x, y in pairs)
True

或者获取不同的配对:
>>> [(x, y) for x, y in pairs if x != y]
[({'key3': 'EEE', 'key2': 'BBB', 'key1': 'AAA', 'unique_id': '001'}, {'key3': 'EEE', 'key2': 'DDD', 'key1': 'AAA', 'unique_id': '001'})]

你甚至可以获取每对中不匹配的键。
>>> [[k for k in x if x[k] != y[k]] for x, y in pairs if x != y]
[['key2']]

可能还包括相关的数值:
>>> [[(k, x[k], y[k]) for k in x if x[k] != y[k]] for x, y in pairs if x != y]
[[('key2', 'BBB', 'DDD')]]

注意:如果您的输入列表尚未排序,您也可以轻松地完成排序:

>>> from operator import itemgetter
>>> list_1, list_2 = [sorted(l, key=itemgetter('unique_id')) 
                      for l in (list_1, list_2)]

@Lattyware:是的,我假设这些列表是对齐的,匹配的字典在两个列表中具有相同的位置。我认为这就是 OP 面临的情况。 - Niklas B.
@NiklasB。当然,但是为了完整起见,我想提一下它。 - Gareth Latty
@Niklas B:是的,这些列表已经排序了。但如果没有排序怎么办呢?如果列表没有被排序,我该怎么做? - alwbtc
8
值得一提的是,ZIP 方法仅在每个列表中具有相同数量的字典时才有效。 - Doomsday
@Doomsday:不过如果不是这种情况,输出应该是什么并不清楚。 - Niklas B.
@NiklasB。我会认为在list_1list_2中缺失的dicts是差异输出的一部分。 - gies0r

19

最快、最全面的方法是使用两个 settuple

set_list1 = set(tuple(sorted(d.items())) for d in sorted(list1))
set_list2 = set(tuple(sorted(d.items())) for d in sorted(list2))
    

(如果您的列表已经是排序的,只需删除列表排序即可节省性能)

使用intersection查找重叠

set_overlapping = set_list1.intersection(set_list2)

使用 symmetric_difference 查找差异

set_difference = set_list1.symmetric_difference(set_list2)

元组(tuple)转换回字典(dict)

 for tuple_element in set_difference:
     list_dicts_difference.append(dict((x, y) for x, y in tuple_element))

@gies0r 我该如何更改代码的最后一部分以实现跳过?我只想获取匹配的键而不是值! - eabanoz
假设list1和list2已经排序,你需要在set_list1set_list2上调用sort。 - Mark
@Mark:正在处理代码中的排序列表 - 感谢 - 已编辑。 - gies0r

3
以下是字典的比较结果,并打印出不相等的条目:
for d1, d2 in zip(list_1, list_2):
    for key, value in d1.items():
        if value != d2[key]:
            print key, value, d2[key]

输出:key2 BBB DDD。通过使用zip,我们可以同时迭代两个字典。然后,我们迭代第一个字典的项,并将其值与第二个字典中相应的值进行比较。如果它们不相等,则打印键和两个值。

它是如何知道应该使用相同的“unique_id”键来比较字典的呢? - alwbtc
1
根据示例,我假设包含字典的列表是有序的。如果不是这种情况,则需要先按unique_id排序。 - Simeon Visser

1
我有一个实际上不依赖于特定键的版本,所以元素要么相等(为零),要么不相等(非零):
list_1 = [{'unique_id':'001', 'key1':'AAA', 'key2':'BBB', 'key3':'EEE'}, {'unique_id':'002', 'key1':'AAA', 'key2':'CCC', 'key3':'FFF'}]
list_2 = [{'unique_id':'001', 'key1':'AAA', 'key2':'DDD', 'key3':'EEE'}, {'unique_id':'002', 'key1':'AAA', 'key2':'CCC', 'key3':'FFF'}]
list_3 = [{'Name': 'Abid', 'Age': 27},{'Name': 'Mahnaz', 'Age': 27}]
list_4 = [{'Name': 'Abid', 'Age': 27},{'Name': 'Mahnaz', 'Age': 27}]

print cmp(list_1,list_1)
print cmp(list_1,list_3)
print cmp(list_1,list_2)
print cmp(list_2,list_1)
print cmp(list_3,list_4)

给出:
Return Value :  0
Return Value :  1
Return Value : -1
Return Value :  1
Return Value :  0

cmp() 函数是做什么的?你可以提供 cmp() 函数的实现吗? - Shubham Srivastava
@ShubhamSrivastava:你可以从官方信息中了解它:“cmp(x,y)比较两个对象x和y,并根据结果返回一个整数。如果x<y,则返回负值;如果x==y,则返回零;如果x>y,则返回正值。”https://docs.python.org/2/library/functions.html#cmp - alemol

0
def new_change(old_list, new_list):
    change_list = []
    for x in new_list:
        for y in old_list:
            if x['unique_id'] != y['unique_id']:
                change_list.append(x)
    return change_list

在这个方法中传递旧列表和新列表


0
Let list1 = []
list2 = []

To fetch all the key values we can do like this:
key_values = list1[0]
key = key_values.keys() //key is a list and contains all key values

below is a piece of code which compares all the key pair values:

for val in list1:
    first_key = key[0]
    for val2 in list2:
        if val2[first_key] == val[first_key]:
            for val3 in key:
                if val2[val3] != val[val3]:
                    Unmatched.append(val)

print unmatched

Above contains matches dictionary and prints for which all key, pair values didn't match.

0
和jvperrin一样,但设置更加合理。
def are_list_of_dicts_the_same(list1, list2):
    pairs = zip(list1, list2)
    return all(x == y for x, y in pairs)

根据目前的表述,你的回答不够清晰。请编辑以添加更多细节,以帮助他人理解如何解答所提出的问题。你可以在帮助中心找到关于如何撰写好回答的更多信息。 - undefined

-1

基于键值比较两个字典列表,并从另一个字典中获取不同的字典项::-------

db_items = [
{
    "id": "db...1111",
    "name": "amit",
    "type": "cricket",
    "value": "kumar"
},
{
    "id": "db...222",
    "name": "rishabh",
    "type": "traveller",
    "value": "gupta"
},
{
    "id": "items.....2222",
    "name": "raj",
    "type": "foodie",
    "value": "ankur"
}
]
items = [
{
    "id": "items.....1111",
    "name": "abhishek",
    "type": "cricket",
    "value": "sharma"
},
{
    "id": "items.....2222",
    "name": "hardik",
    "type": "traveller",
    "value": "sharma"
},
{
    "id": "db...333",
    "name": "shanchay",
    "type": "music",
    "value": "kumar"
}
]
for item in items.copy():
for i in db_items:
    if item.get("type") == i.get("type"):
        items.remove(item)

print(items)

output:---
[{'id': 'db...333', 'name': 'shanchay', 'type': 'music', 'value': 
'kumar'}]

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