Преобразование списка в набор изменений порядка элементов - PullRequest
79 голосов
/ 20 марта 2012

Недавно я заметил, что при преобразовании list в set порядок элементов изменяется и сортируется по символам.

Рассмотрим этот пример:

x=[1,2,20,6,210]
print x 
# [1, 2, 20, 6, 210] # the order is same as initial order

set(x)
# set([1, 2, 20, 210, 6]) # in the set(x) output order is sorted

Мои вопросы -

  1. Почему это происходит?
  2. Как я могу выполнять операции с множествами (особенно с разницей в множествах), не теряя первоначальный порядок?

Ответы [ 8 ]

81 голосов
/ 20 марта 2012
  1. A set - неупорядоченная структура данных.

  2. Не используйте set, а скорее collections.OrderedDict:

    >>> a = collections.OrderedDict.fromkeys([1, 2, 20, 6, 210])
    >>> b = collections.OrderedDict.fromkeys([6, 20, 1])
    >>> collections.OrderedDict.fromkeys(x for x in a if x not in b)
    OrderedDict([(2, None), (210, None)])
    

    Обратите внимание, что порядок b не имеет значения, поэтому он может быть любым итеративным, но он должен быть итерируемым, который поддерживает тесты членства O (1).

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

>>> a = [1, 2, 20, 6, 210]
>>> b = set([6, 20, 1])
>>> [x for x in a if x not in b]
[2, 210]

Это теряет порядок b, не позволяет быстрые тесты членства на a и результат. Наборы позволяют быстро проверять членство, а списки поддерживают порядок. Если вам нужны обе эти функции в одной коллекции, используйте collections.OrderedDict.

31 голосов
/ 29 декабря 2016

В Python 3.6 set() сейчас должен сохранять порядок, но есть другое решение для Python 2 и 3:

>>> x = [1, 2, 20, 6, 210]
>>> sorted(set(x), key=x.index)
[1, 2, 20, 6, 210]
14 голосов
/ 20 марта 2012

Отвечая на ваш первый вопрос, множество - это структура данных, оптимизированная для операций над множествами.Как и математический набор, он не обеспечивает или не поддерживает какой-либо конкретный порядок элементов.Абстрактная концепция набора не обеспечивает порядок, поэтому реализация не обязательна.Когда вы создаете набор из списка, Python может изменять порядок элементов для нужд внутренней реализации, которую он использует для набора, который способен эффективно выполнять операции над множествами.

3 голосов
/ 20 марта 2012

Как указано в других ответах, наборы - это структуры данных (и математические понятия), которые не сохраняют порядок элементов -

Однако, используя комбинацию наборов и словарей, возможно, что вы сможете достичь того, чего захотите - попробуйте использовать следующие фрагменты:

# save the element order in a dict:
x_dict = dict(x,y for y, x in enumerate(my_list) )
x_set = set(my_list)
#perform desired set operations
...
#retrieve ordered list from the set:
new_list = [None] * len(new_set)
for element in new_set:
   new_list[x_dict[element]] = element
1 голос
/ 01 мая 2018

Реализация концепции наивысшей оценки, приведенной выше, которая возвращает ее к списку:

def SetOfListInOrder(incominglist):
    from collections import OrderedDict
    outtemp = OrderedDict()
    for item in incominglist:
        outtemp[item] = None
    return(list(outtemp))

Протестировано (кратко) на Python 3.6 и Python 2.7.

1 голос
/ 30 января 2015

Опираясь на ответ Свена, я обнаружил, что с помощью коллекций .OrderedDict вроде как помог мне выполнить то, что вы хотите, плюс позволил мне добавить больше элементов к диктовке:

import collections

x=[1,2,20,6,210]
z=collections.OrderedDict.fromkeys(x)
z
OrderedDict([(1, None), (2, None), (20, None), (6, None), (210, None)])

Если вы хотите добавить предметы, но по-прежнему относитесь к ним как к набору, вы можете просто сделать:

z['nextitem']=None

И вы можете выполнить такую ​​операцию, как z.keys (), и получить набор:

z.keys()
[1, 2, 20, 6, 210]
0 голосов
/ 22 мая 2019

Если у вас есть небольшое количество элементов в двух ваших начальных списках, для которых вы хотите выполнить операцию установки разницы вместо использования collections.OrderedDict, что усложняет реализацию и делает ее менее читаемой, вы можете использовать:

# initial lists on which you want to do set difference
>>> nums = [1,2,2,3,3,4,4,5]
>>> evens = [2,4,4,6]
>>> evens_set = set(evens)
>>> result = []
>>> for n in nums:
...   if not n in evens_set and not n in result:
...     result.append(n)
... 
>>> result
[1, 3, 5]

Его временная сложность не так хороша, но она аккуратна и легко читается.

0 голосов
/ 07 июля 2016

Вот простой способ сделать это:

x=[1,2,20,6,210]
print sorted(set(x))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...