将列表转换为集更改元素顺序

d.p*_*tto 94 python set

最近我注意到,当我转换listset元素的顺序发生变化,由字符排序.

考虑这个例子:

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
Run Code Online (Sandbox Code Playgroud)

我的问题是 -

  1. 为什么会这样?
  2. 如何在不丢失初始订单的情况下进行设置操作(尤其是设置差异)?

Sve*_*ach 90

  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)])
    
    Run Code Online (Sandbox Code Playgroud)

    请注意,顺序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]
Run Code Online (Sandbox Code Playgroud)

这会丢失顺序b,不允许快速成员资格测试a和结果.集允许快速成员资格测试,列表保持顺序.如果您在同一个集合中需要这两个功能,请使用collections.OrderedDict.

  • @肖恩不,他们没有。“None”是一种语言保证的单例。在 CPython 中,实际成本只是指针(尽管该成本始终存在,但对于字典,您几乎可以考虑“无”和其他单例或“免费”共享引用),因此一个机器字,可能是 8 个字节在现代计算机上。但是,是的,它不像套装那样节省空间。 (3认同)
  • 无对象花费16个字节.如果只有默认的OrderedSet().:( (2认同)
  • 在 CPython 3.6+ 上,你可以只执行 `dict.fromkeys([1, 2, 1]).keys()` 因为常规的 `dict` 也保留顺序。 (2认同)
  • @Sven 我说的是 CPython。我到处发布这个,我只是厌倦了编写“CPython 3.6 或任何其他从 Python 3.7 开始的实现”。没关系,大家都在用CPython (2认同)

Tig*_*222 39

在Python 3.6中,set()现在应该保持顺序,但是 Python 2和3还有另一个解决方案:

>>> x = [1, 2, 20, 6, 210]
>>> sorted(set(x), key=x.index)
[1, 2, 20, 6, 210]
Run Code Online (Sandbox Code Playgroud)

  • @ThijsvanDien这是错的,`set()`在Python 3.6中没有排序,甚至不作为实现细节,你在想``dict`s (21认同)
  • 关于订单保存的两个注意事项:仅从Python 3.6开始,甚至在那里,它被认为是一个实现细节,所以不要依赖它.除此之外,您的代码效率非常低,因为每次调用`x.index`时,都会执行线性搜索.如果你对二次复杂度很好,那么首先没有理由使用`set`. (8认同)
  • 我不明白为什么这个答案有这么多的赞成票,它不保持插入顺序,也不返回一组。 (8认同)
  • @ThijsvanDien没有他们没有排序,虽然有时看起来是因为`int经常哈希自己/sf/ask/3190733101/ (6认同)
  • 这是一个极其低效的 O(n^2) 时间复杂度。 (3认同)
  • 因此,您执行一行代码,最终得到的输出与您开始时的输入相同……为什么这有 70 多个赞成票? (3认同)
  • 尝试`x = [1,2,-1,20,6,210]`并使其成为一组.你会发现它根本没有订购,在Python 3.6中测试过. (2认同)

小智 19

通过以下功能删除重复项并保留顺序

def unique(sequence):
    seen = set()
    return [x for x in sequence if not (x in seen or seen.add(x))]
Run Code Online (Sandbox Code Playgroud)

如何在Python中保留顺序的同时从列表中删除重复项

  • 这正是我使用 set 的目的,这解决了使用 set 从列表中删除重复项的主要问题;失去原来的列表顺序。 (3认同)

lve*_*lla 16

回答第一个问题,集合是针对集合操作优化的数据结构.像数学集一样,它不强制执行或维护元素的任何特定顺序.集合的抽象概念不强制执行顺序,因此不需要实现.当您从列表创建集合时,Python可以自由地更改元素的顺序,以满足其用于集合的内部实现的需要,从而能够有效地执行集合操作.


Ale*_*rdi 15

你可以用一行代码删除重复的值并保持插入的列表顺序,Python 3.8.2

mylist = ['b', 'b', 'a', 'd', 'd', 'c']


结果 = list({value:"" for mylist 中的值})

打印(结果)

>>> ['b'、'a'、'd'、'c']

结果 = 列表(dict.fromkeys(mylist))

打印(结果)

>>> ['b'、'a'、'd'、'c']

  • 这是最好的单衬解决方案 (3认同)

pyl*_*ang 14

在数学中,有集合有序集合(oset)。

  • set : 唯一元素的无序容器(已实现)
  • oset:独特元素的有序容器(NotImplemented)

在 Python 中,只直接实现了集合。我们可以使用常规的 dict 键(3.7+)模拟 oset 。

给定的

a = [1, 2, 20, 6, 210, 2, 1]
b = {2, 6}
Run Code Online (Sandbox Code Playgroud)

代码

oset = dict.fromkeys(a).keys()
# dict_keys([1, 2, 20, 6, 210])
Run Code Online (Sandbox Code Playgroud)

演示

删除重复项,保留插入顺序。

list(oset)
# [1, 2, 20, 6, 210]
Run Code Online (Sandbox Code Playgroud)

dict 键上的类似设置的操作。

oset - b
# {1, 20, 210}

oset | b
# {1, 2, 5, 6, 20, 210}

oset & b
# {2, 6}

oset ^ b
# {1, 5, 20, 210}
Run Code Online (Sandbox Code Playgroud)

细节

注意:无序结构不排除有序元素。相反,不保证维持秩序。例子:

assert {1, 2, 3} == {2, 3, 1}                    # sets (order is ignored)
Run Code Online (Sandbox Code Playgroud)

assert [1, 2, 3] != [2, 3, 1]                    # lists (order is guaranteed)
Run Code Online (Sandbox Code Playgroud)

人们可能会很高兴地发现列表多集(mset) 是两种更迷人的数学数据结构:

  • list:允许复制的有序元素容器(已实现)
  • mset:允许复制的无序元素容器 (NotImplemented)*

概括

Container | Ordered | Unique | Implemented
----------|---------|--------|------------
set       |    n    |    y   |     y
oset      |    y    |    y   |     n
list      |    y    |    n   |     y
mset      |    n    |    n   |     n*  
Run Code Online (Sandbox Code Playgroud)

*多重集可以间接模拟collections.Counter(),一个类似字典的多重性(计数)映射。


jsb*_*eno 6

如其他答案中所述,集合是不保留元素顺序的数据结构(和数学概念)-

但是,通过使用集合和字典的组合,您可以实现任何您想要的 - 尝试使用这些片段:

# 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
Run Code Online (Sandbox Code Playgroud)