Python 将相等的列表元素组合在一起

Python 将相等的列表元素组合在一起,python,list,Python,List,这是将相等的元素组合在一起(使它们在列表中连续出现)的好方法吗 编辑:列出据称不起作用的地方(见评论): 只需使用列表。排序: a = [2, 7, 1, 8, 2, 8, 1, 8, 2, 8] a.sort() print(a) a = [2, 7, 1, 8, 2, 8, 1, 8, 2, 8] a.sort(reverse = True) print(a) 输出: [1, 1, 2, 2, 2, 7, 8, 8, 8, 8] [8, 8, 8, 8, 7, 2, 2, 2, 1,

这是将相等的元素组合在一起(使它们在列表中连续出现)的好方法吗

编辑:列出据称不起作用的地方(见评论):


只需使用列表。排序:

a = [2, 7, 1, 8, 2, 8, 1, 8, 2, 8]
a.sort()
print(a)
a = [2, 7, 1, 8, 2, 8, 1, 8, 2, 8]
a.sort(reverse = True)
print(a)
输出:

[1, 1, 2, 2, 2, 7, 8, 8, 8, 8]
[8, 8, 8, 8, 7, 2, 2, 2, 1, 1]
如果要按降序排列,请将
reverse=True
传递给
列表。排序

a = [2, 7, 1, 8, 2, 8, 1, 8, 2, 8]
a.sort()
print(a)
a = [2, 7, 1, 8, 2, 8, 1, 8, 2, 8]
a.sort(reverse = True)
print(a)
输出:

[1, 1, 2, 2, 2, 7, 8, 8, 8, 8]
[8, 8, 8, 8, 7, 2, 2, 2, 1, 1]

另一种解决方案,不保证输出的顺序,但对于使用sort:count值的解决方案,是O(n)而不是O(n*log(n)),并创建一个新列表,其中包含每个值的相应计数:

from collections import Counter

a = [2, 7, 1, 8, 2, 8, 1, 8, 2, 8]

counts = Counter(a)
out = []
for value, count in counts.items():
    out.extend([value]*count)
    
print(out)
# [2, 2, 2, 7, 1, 1, 8, 8, 8, 8]

正如@Manuel所建议的,有一种我从未注意到的
计数器
方法:

因此,要以原始顺序获得输出,在O(n)中,代码简单如下:

from collections import Counter

a = [2, 7, 1, 8, 2, 8, 1, 8, 2, 8]

out = list(Counter(a).elements())

print(out)
# [2, 2, 2, 7, 1, 1, 8, 8, 8, 8]

为什么/如何工作,以及这可能是一个未定义的实现细节:

迭代器:

然后
for
循环向迭代器请求一个项。在得到最后的8之后,迭代器将其索引减少到列表中并返回8(),for
循环将其存储在
x
中。现在我们有:

                             ↓
a = [2, 7, 1, 8, 2, 8, 1, 8, 2, 8]
x = 8
然后
a.remove(x)
删除第一个8,这会将后面的所有项目向左移动:

                             ↓
a = [2, 7, 1, 2, 8, 1, 8, 2, 8]
x = 8
a.append(x)
将其追加到末尾:

                             ↓
a = [2, 7, 1, 2, 8, 1, 8, 2, 8, 8]
x = 8
然后
for
循环从迭代器中获取下一项,与前面的8相同,仅在较低的索引处:

                          ↓
a = [2, 7, 1, 2, 8, 1, 8, 2, 8, 8]
x = 8 (same one again)
remove
再次删除第一个8(原来是第二个8):

它被附加了:

                          ↓
a = [2, 7, 1, 2, 1, 8, 2, 8, 8, 8]
下一轮将原来的第三个8移动到末尾:

                       ↓
a = [2, 7, 1, 2, 1, 2, 8, 8, 8, 8]
最后,最初的第四个8(我们已经一遍又一遍地发现)也开始移动:

                    ↓
a = [2, 7, 1, 2, 1, 2, 8, 8, 8, 8]
同样的情况也会发生在2、1和7上,因此我们最终得到:

a = [8, 8, 8, 8, 2, 2, 2, 1, 1, 7]
JBernardo评论说,这“可能是不同python实现上未定义的行为”。我想可能是这样,但我会责怪它的实现。Python参考文档(尽管是关于前向迭代器的):

当循环修改序列时有一个微妙之处(这只能发生在可变序列中,例如列表)。内部计数器用于跟踪下一个使用哪个项,并且在每次迭代中递增。当该计数器达到序列长度时,循环终止。这意味着,如果套件从序列中删除当前(或上一个)项,则将跳过下一个项(因为它获取已处理的当前项的索引)。同样,如果套件在当前项之前的序列中插入了一个项,那么下次通过循环将再次处理当前项


这并没有被标记为CPython实现细节,Python文档在很多其他地方都有这样的功能:261个google结果

您不应该同时迭代和修改列表。为什么你不能把它分类?@JBernardo是的,我同意。但你知道为什么这会起作用吗?我希望不会有任何更改,或者有一个缺少项的列表,但没有一个包含聚集项的列表。@JBernardo-Bah,那就没那么有趣了。但是是的,我把事情搞砸了。我想我可以把“按最后一次出现的顺序”作为一个要求,尽管这只会使排序变得不那么方便。@Asocia它可以工作,因为反向迭代器从最后一次到第一次保留索引值,并且总是使用remove+append保持列表的大小相同。但这可能是不同python上未定义的行为implementation@Asocia有关详细解释,请参阅我的答案。Python 3.6+@JBernardo上保证了顺序,这是正确的,在本例中,输出将按照值在原始列表中出现的顺序进行分组。也可以使用
元素()
方法。@Manuel谢谢,我从来没有注意到这一点!我把它加到了答案上。
a = [8, 8, 8, 8, 2, 2, 2, 1, 1, 7]