Получение разницы между 2 списками, которые содержат словари

list1 = [{'key1': 'item1'}, {'key2': 'item2'}]
list2 = [{'key1': 'item1'}, {'key2': 'item2'}, {'key3': 'item3'}]

Есть ли способ получить разницу между этими двумя списками?

По сути, мне нужен масштабируемый способ получить различия между двумя списками, которые содержат словари. Поэтому я пытаюсь сравнить эти списки и просто получить возвращение {'key3': 'item3'}

Ответы

Ответ 1

Вы можете использовать понимание списка:

list1 = [{'key1': 'item1'}, {'key2': 'item2'}]
list2 = [{'key1': 'item1'}, {'key2': 'item2'}, {'key3': 'item3'}]

print([x for x in list2 if x not in list1])

Что даст [{'key3': 'item3'}]

Ответ 2

Вы можете использовать set() с пониманием, как этот пример:

def get_diff(elm1, elm2):
    a = set((m, n) for k in elm1 for m, n in k.items())
    b = set((m, n) for k in elm2 for m, n in k.items())
    if len(b) > len(a):
        return dict(b - a)
    return dict(a - b)


list1 = [{'key1': 'item1'}, {'key2': 'item2'}]
list2 = [{'key1': 'item1'}, {'key2': 'item2'}, {'key3': 'item3'}]
get_diff(list1, list2)

Выход:

{'key3': 'item3'}

Ответ 3

in_list1_not_in_list2 = [i for i in list1 if i not in list2]
in_list2_not_in_list1 = [i for i in list2 if i not in list1]

Ответ 4

Поскольку словари не могут быть хешируемыми, хэшировать не так просто, но так как у нас есть один ключ и одно значение на каждый словарь, мы можем создать наш собственный ключ! Так что вы можете сделать что-то вроде этого:

list1_set = set()

for dictionary in list1:
    key = dictionary.keys()[0]
    vals = dictionary.values()[0]
    custom_key = '{}|{}'.format(key,vals)
    list1_set.add(custom_key)

differences = []
for dictionary in list2:
    key = dictionary.keys()[0]
    vals = dictionary.values()[0]
    custom_key = '{}|{}'.format(key,vals)

    if custom_key not in list1_set:
        differences.append(dictionary)

print differences

выход:

[{'key3': 'item3'}]

Не это решение гораздо более масштабируемо, чем просто повторение первого списка из-за возможности постоянного поиска.

Ответ 5

Вы можете сообщить словарю, как хэшировать себя, а затем вы можете использовать наборы

import json

class HashableDict(dict):   
    def __hash__(self):
        # convert the dictionary to something hashable - in this case a str
        return hash(json.dumps(self)) 

тогда вы можете сделать

hashable_list1 = map(HashableDict, list1)
hashable_list2 = map(HashableDict, list2)
set(hashable_list2).difference(hashable_list1)

difference дает вам элементы в lists2, которых нет в list1.

Если вам нужна вся разница, поэтому все элементы, которых нет в обоих списках, выполните:

set(hashable_list2).symmetric_difference(hashable_list1)

Обратите внимание, что это не будет работать для всех словарей (например, словарей, содержащих объекты, с json.dumps не может работать), если вы не обрабатываете их явно с помощью специального JSONEncoder

Ответ 6

Вы также можете попробовать использовать set.symmetric_difference() чтобы получить разницу между наборами в обоих направлениях:

list1 = [{'key1': 'item1'}, {'key2': 'item2'}]
list2 = [{'key1': 'item1'}, {'key2': 'item2'}, {'key3': 'item3'}]

set1 = set(tuple(x.items())[0] for x in list1)
set2 = set(tuple(x.items())[0] for x in list2)

print([dict(list(set1.symmetric_difference(set2)))])
# [{'key3': 'item3'}]

print([dict(list(set2.symmetric_difference(set1)))])
# [{'key3': 'item3'}]

Другим способом было бы использовать itertools.filterfalse():

from itertools import filterfalse

diff1 = list(filterfalse(lambda d: d in list2, list1))
diff2 = list(filterfalse(lambda d: d in list1, list2))

print(diff1 + diff2)
# [{'key3': 'item3'}]