Для многих случаев использования вам нужен ответ:
ys = set(y)
[item for item in x if item not in ys]
Это гибрид между ответом Ааронастерлинга и ответом QuantSoup .
версия aaronasterling выполняет len(y)
сравнение элементов для каждого элемента в x
, поэтому требуется квадратичное время. В версии QuantSoup используются наборы, поэтому для каждого элемента в x
выполняется поиск по одному набору с постоянным временем, но, поскольку он преобразует и x
, и y
в наборы, он теряет порядок ваши элементы.
Преобразуя только y
в набор и повторяя по порядку x
, вы получаете лучшее из обоих миров - линейного времени и сохранения порядка. *
Тем не менее, в версии QuantumSoup все еще есть проблема: она требует, чтобы ваши элементы были хэшируемыми. Это в значительной степени встроено в природу наборов. ** Если вы пытаетесь, например, вычесть список диктов из другого списка, но список для вычитания велик, что вы делаете?
Если вы можете украсить ваши значения так, чтобы они были хэшируемыми, это решит проблему. Например, для плоского словаря, значения которого можно хэшировать:
ys = {tuple(item.items()) for item in y}
[item for item in x if tuple(item.items()) not in ys]
Если ваши типы немного сложнее (например, вы часто имеете дело с JSON-совместимыми значениями, которые можно хэшировать, или списками или диктовками, значения которых имеют рекурсивный тип), вы все равно можете использовать это решение. Но некоторые типы просто не могут быть преобразованы во что-либо хешируемое.
Если ваши элементы не являются и не могут быть сделаны хэшируемыми, но они сопоставимы, вы можете получить, по крайней мере, логарифмическое время (O(N*log M)
, что намного лучше, чем O(N*M)
время решение списка, но не так хорошо, как O(N+M)
время заданного решения) путем сортировки и использования bisect
:
ys = sorted(y)
def bisect_contains(seq, item):
index = bisect.bisect(seq, item)
return index < len(seq) and seq[index] == item
[item for item in x if bisect_contains(ys, item)]
Если ваши предметы не являются ни хешируемыми, ни сопоставимыми, значит, вы застряли с квадратичным решением.
* Обратите внимание, что вы также можете сделать это, используя пару объектов OrderedSet
, для которых вы можете найти рецепты и сторонние модули. Но я думаю, что это проще.
** Причина, по которой установленные поиски являются постоянными, состоит в том, что все, что нужно сделать, - это хэшировать значение и посмотреть, есть ли запись для этого хэша. Если он не может хэшировать значение, это не сработает.