将所有`None`移动到列表末尾(python3)

时间:2017-08-03 05:20:29

标签: python python-3.x list

给出像

这样的列表
[None, None, 90, 10, None, 34, None, 108]

在不影响其他元素顺序的情况下,将所有None移动到最后的简单方法是什么?所以,你得到像

这样的东西
[90, 10, 34, 108, None, None, None, None]

一种可能的解决方案是将filterNone一起使用,然后在最后添加None

In [890]: l2 = list(filter(None, l))

In [891]: l2 += [None] * (len(l) - len(l2))

In [892]: l2
Out[892]: [90, 10, 34, 108, None, None, None, None]

然而,

  

filter(None, ...)并不代表"过滤掉None s&#34 ;; None是   特殊情况下的身份谓词,所以这也将过滤掉   0,False以及从__bool__返回False的任何其他内容。

所以,这不是一个好的解决方案。还有什么更好的吗?

6 个答案:

答案 0 :(得分:8)

保留列表的排序:

>>> l = [None, None, 90, 10, None, 34, None, 108]
>>> sorted(l, key = lambda x: x is None)
[90, 10, 34, 108, None, None, None, None]

因为,引用python docs

  

内置的sorted()函数保证稳定。如果排序保证不更改比较相等的元素的相对顺序,则排序是稳定的 - 这有助于在多个过程中进行排序(例如,按部门排序,然后按工资等级排序)。

答案 1 :(得分:5)

我不知道Big O但是这个:

l2 = [x for x in l if x is not None]
l2 += [None] * l.count(None)

正常工作,

l2.extend([None] * l.count(None))

受到许多蟒蛇的喜爱和

l2 += [None] * (len(l) - len(l2))

better

答案 2 :(得分:5)

我建立一个没有Nones的列表并填充它:

l2 = [x for x in l if x is not None]
l2 += [None] * (len(l) - len(l2))

或者,这是一个单行。我认为它在理论上是O(nlogn),但我没有观察到在实践中 - 我认为Timsort的疾驰模式将所涉及的比较减少到接近但不太接近O(n)的比较,并且数据移动成本太低,不足以支配运行时,即使它们是O(nlogn)。在实践中,更大的缺点可能是你必须考虑如何订购布尔值:

sorted(l, key=lambda x: x is None)

答案 3 :(得分:3)

让你夜不能寐的解决方案:

>>> example = [None, None, 90, 10, None, 34, None, 108]
>>> 
>>> def some(a, *b):
...     return ([*some(*b), a] if a is None else [a, *some(*b)]) if b else [a]
... 
>>> print(some(*example))
[90, 10, 34, 108, None, None, None, None]

<强>解释

我们使用*example将数据传递给我们的函数,以便列表的元素成为参数。然后,我们使用(a, *b)在函数调用中对这些参数进行分段,将第一个参数放在a中,其余参数放在b中。递归地,如果aNone,那么答案是:

[*some(*b), a]

a移至最后。否则,答案是:

[a, *some(*b)]

a保留在前面并处理列表的其余部分。

最后,我们有if b else [a]位,这只是我们递归的基本情况。

<强>烘烤关闭

我们有足够的解决方案,是时候进行烘烤了。 (如果我歪曲了你的解决方案,请告诉我们。)我们将总结10,000个元素列表的10个时间,其中30%的元素是None。我已经从数据中删除了零,以便每个人都可以玩:

import sys
import timeit
import random

A = [random.randint(1, 1000) if random.random() > 0.3 else None for _ in range(10000)]  # ~30% None

def Rahul_comprehension(A):

    B = [x for x in A if x is not None]
    B += [None] * A.count(None)

    return B

def coldspeed_filter(A):

    B = list(filter(None, A))
    B += [None] * (len(A) - len(B))

    return B

def user2357112_comprehension(A):

    B = [x for x in A if x is not None]
    B += [None] * (len(A) - len(B))

    return B

sys.setrecursionlimit(100000)
def cdlane_recursion(A):

    def some(a, *b):
        return ([*some(*b), a] if a is None else [a, *some(*b)]) if b else [a]

    return some(*A)

from functools import reduce
def PaulMcG_reduce(A):

    return sum(reduce(lambda a,b: a[b is None].append(b) or a, A, ([], [])), [])

from itertools import zip_longest
def PaulMcG_zip_longest(A):

    non_nones = filter(lambda x: x is not None, A)
    return next(zip(*zip_longest(non_nones, A)))

def Massimiliano_sorted(A):

    return sorted(A, key=lambda x: x is None)

# sanity check to make sure everyone agrees on the answer
B = Rahul_comprehension(A)  # the accepted answer
assert B == coldspeed_filter(A)
assert B == user2357112_comprehension(A)
assert B == cdlane_recursion(A)
assert B == PaulMcG_reduce(A)
assert B == list(PaulMcG_zip_longest(A))
assert B == Massimiliano_sorted(A)

print("Rahul_comprehension:", format(timeit.timeit('B = Rahul_comprehension(A)', number=10, globals=globals()), ".2"))
print("coldspeed_filter:", format(timeit.timeit('B = coldspeed_filter(A)', number=10, globals=globals()), ".2"))
print("user2357112_comprehension:", format(timeit.timeit('B = user2357112_comprehension(A)', number=10, globals=globals()), ".2"))
print("cdlane_recursion:", format(timeit.timeit('B = cdlane_recursion(A)', number=10, globals=globals()), ".2"))
print("PaulMcG_reduce:", format(timeit.timeit('B = PaulMcG_reduce(A)', number=10, globals=globals()), ".2"))
print("PaulMcG_zip_longest:", format(timeit.timeit('B = PaulMcG_zip_longest(A)', number=10, globals=globals()), ".2"))
print("Massimiliano_sorted:", format(timeit.timeit('B = Massimiliano_sorted(A)', number=10, globals=globals()), ".2"))

排序典型最佳运行:(最低最佳)

coldspeed (filter):          0.002
user2357112 (comprehension): 0.0041
Rahul (comprehension):       0.0061
PaulMcG (zip_longest):       0.024
PaulMcG (reduce):            0.025
Massimiliano (sorted):       0.026
cdlane (recursion):          5.8

答案 4 :(得分:1)

此解决方案使用itertools.zip_longest填充None s的两个序列中较短的一个。第一个序列是原始列表中的非None项,第二个序列是完整原始序列。 zip_longest将返回每个序列的项目元组,填充为None,两者中较短的一个。然后我们重新压缩那些元组以获取序列,并取第一个序列,表示根据需要填充尽可能多None s的过滤序列,与原始序列一样长。

>>> from itertools import zip_longest
>>>
>>> seq = [None, None, 90, 10, None, 34, None, 108]
>>>
>>> non_nones = filter(lambda x: x is not None, seq)
>>>
>>> reordered = next(zip(*zip_longest(non_nones, seq)))
>>>
>>> print(reordered)
(90, 10, 34, 108, None, None, None, None)

答案 5 :(得分:1)

@cdlane的解决方案让我想到了使用reduce

>>> seq = [None, None, 90, 10, None, 34, None, 108]
>>>
>>> from functools import reduce
>>> reordered = sum(reduce(lambda a,b: (a[0], a[1]+(b,)) 
                                       if b is None else (a[0]+(b,), a[1]), 
                           seq, ((), ())), ())
>>> print(reordered)
(90, 10, 34, 108, None, None, None, None)

编辑: 我无法想到所有那些元组添加,这是更好的:

>>>> reordered = sum(reduce(lambda a,b: a[b is None].append(b) or a, seq, ([], [])), [])