例如,listto_be包括:3 of"a"、4 of"b"、3 of"c"、5 of"d"

to_be = ["a", "a", "a", "b", "b", "b", "b", "c", "c", "c", "d", "d", "d", "d", "d", ...]

现在我希望是这样:
done = ["a", "b", "c", "d", ... , "a", "b", "c", "d", ... , "b", "d", ...] (notice: some items are more than others as in amounts, but they need to be still in a pre-defined order, alphabetically for example)

最快的方法是什么?

最佳答案

假设我了解您的需求,可以通过组合itertools.zip_longestitertools.groupbyitertools.chain.from_iterable()相对容易地完成:
我们首先将这些项分组(如"a"s、"b"s等等),然后将它们按您想要的顺序压缩(每组一个),使用chain生成单个列表,然后删除压缩所引入的None值。

>>> [item for item in itertools.chain.from_iterable(itertools.zip_longest(*[list(x) for _, x in itertools.groupby(to_be)])) if item]
['a', 'b', 'c', 'd', 'a', 'b', 'c', 'd', 'a', 'b', 'c', 'd', 'b', 'd', 'd']

您可能希望将其中的一些list comprehensions分离出来,使其更具可读性,但是:
>>> groups = itertools.zip_longest(*[list(x) for _, x in itertools.groupby(to_be)])
>>> [item for item in itertools.chain.from_iterable(groups) if item]
['a', 'b', 'c', 'd', 'a', 'b', 'c', 'd', 'a', 'b', 'c', 'd', 'b', 'd', 'd']

(给定的版本用于3.x,对于2.x,您需要izip_longest()
和往常一样,如果你期望空字符串,0等等…然后您将需要执行if item is not None,如果需要保持None值不变,请创建一个sentinel对象并检查其标识。
您还可以使用文档中给出的the roundrobin() recipe作为压缩的替代,这使得压缩非常简单:
>>> list(roundrobin(*[list(x) for _, x in itertools.groupby(to_be)]))
['a', 'b', 'c', 'd', 'a', 'b', 'c', 'd', 'a', 'b', 'c', 'd', 'b', 'd', 'd']

最后一点,观察者可能会注意到我在groupby()生成器中创建列表,这看起来很浪费,原因来自the docs
返回的组本身就是一个迭代器,它共享底层
可与groupby()一起使用因为源是共享的,当
对象是高级的,以前的组不再可见。
因此,如果以后需要这些数据,应该将其存储为一个列表。

10-07 20:01
查看更多