Сравните списки словарей с вложенными списками в Python - PullRequest
1 голос
/ 02 июня 2019

Я хотел бы получить разницу двух списков, которые имеют следующую структуру:

first_dict = [{"a": "abcd","b":"defg", "c":["fng","xvg"]}, {"a": "stg","b":"klsm", "c":["xzy"]}]

second_dict = [{"a": "abcd","b":"defg", "c":["fng","xvg"]}]

Я пытался заморозить наборы следующим образом:

i_set = { frozenset(row.items()) for row in first_dict }
a_set = { frozenset(row.items())  for row in second_dict }
result = [dict(i) for i in i_set - a_set]

Ожидаемый результат:

v = {"a": "stg","b":"klsm", "c":["xzy"]}

Поскольку "c":[] - это список, я получаю следующую ошибку:

TypeError: unhashable type: 'list'

Я посмотрел похожие ответы здесь и здесь но они не работают для моей проблемы.

Ответы [ 2 ]

1 голос
/ 02 июня 2019

Поскольку вы использовали set и frozenset, я полагаю, что вы заботитесь об эффективности и не хотите O(n**2) алгоритм.

В этом случае вам не нужно просто преобразовывать словари вFrozensets, но также конвертировать списки в класс hashable.Вы можете использовать tuple с.

Предполагая, что ваши входные данные:

first_dict = [{"a": "abcd","b":"defg", "c":["fng","xvg"]}, {"a": "stg","b":"klsm", "c":["xzy"]}]

second_dict = [{"a": "abcd","b":"defg", "c":["fng","xvg"]}]

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

def convert(dictionary):
    return frozenset((key, tuple(value) if isinstance(value, list) else value) for key, value in dictionary.items())

def convert_back(frozset):
    return dict((key, list(value) if isinstance(value, tuple) else value) for key, value in frozset)

i_set = { convert(row) for row in first_dict }
a_set = { convert(row) for row in second_dict }
result = [convert_back(i) for i in i_set - a_set]

Тогда результат будет:

[{'a': 'stg', 'c': ['xzy'], 'b': 'klsm'}]

Однако это не будет различать кортежи, которые были кортежами в оригинале, и кортежи, которые были списками в оригинале.


Вы можете решить это (если вы уверены, чтословарь и его содержимое не видоизменяются во время операций), оборачивая словари в свой собственный класс.Преимущество в том, что вам не нужно конвертировать их потом, вы можете просто «развернуть» значение.

class HashableDictionaryWithListValues:
    def __init__(self, dictionary):
        converted = frozenset((key, tuple(value) if isinstance(value, list) else value) for key, value in dictionary.items())
        self._hash = hash(converted)
        self._converted = converted
        self.dictionary = dictionary

    def __hash__(self):
        return self._hash

    def __eq__(self, other):
        return self._converted == other._converted

i_set = { HashableDictionaryWithListValues(row) for row in first_dict }
a_set = { HashableDictionaryWithListValues(row) for row in second_dict }
result = [i.dictionary for i in i_set - a_set]

Результат будет:

[{'a': 'stg', 'b': 'klsm', 'c': ['xzy']}]

Хотя вы также можете избежать всего преобразования / переноса и использовать O(n**2) подход:

def difference(first, second):
    for item in first:
        if item not in second:
            yield item

list(difference(first_dict, second_dict))

, который также дает ожидаемый результат:

[{'a': 'stg', 'b': 'klsm', 'c': ['xzy']}]
0 голосов
/ 02 июня 2019

Почему frozenset ?, просто используйте:

>>> first_dict = [{"a": "abcd","b":"defg", "c":["fng","xvg"]}, {"a": "stg","b":"klsm", "c":["xzy"]}]
>>> second_dict = [{"a": "abcd","b":"defg", "c":["fng","xvg"]}]
>>> [i for i in first_dict if i not in second_dict]
[{'a': 'stg', 'b': 'klsm', 'c': ['xzy']}]
>>> 

Или, если вы хотите одно единственное, измените:

>>> [i for i in first_dict if i not in second_dict]

На:

>>> [i for i in first_dict if i not in second_dict][0]

Какие выходы:

{'a': 'stg', 'b': 'klsm', 'c': ['xzy']}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...