从列表中删除所有出现的值?

Posted

技术标签:

【中文标题】从列表中删除所有出现的值?【英文标题】:Remove all occurrences of a value from a list? 【发布时间】:2010-11-12 12:40:48 【问题描述】:

在 Python 中,remove() 将删除列表中第一次出现的值。

如何从列表中删除所有个值?

这就是我的想法:

>>> remove_values_from_list([1, 2, 3, 4, 2, 2, 3], 2)
[1, 3, 4, 3]

【问题讨论】:

【参考方案1】:

功能方法:

Python 3.x

>>> x = [1,2,3,2,2,2,3,4]
>>> list(filter((2).__ne__, x))
[1, 3, 3, 4]

>>> x = [1,2,3,2,2,2,3,4]
>>> list(filter(lambda a: a != 2, x))
[1, 3, 3, 4]

Python 2.x

>>> x = [1,2,3,2,2,2,3,4]
>>> filter(lambda a: a != 2, x)
[1, 3, 3, 4]

【讨论】:

在过滤器+lambda上使用列表推导;除了通常更高效之外,前者更具可读性。 s/一般/一般是/ habnabit 的建议代码如下:[y for y in x if y != 2] 我不会将此解决方案称为最佳解决方案。在浏览代码时,列表推导更快、更容易理解。这更像是一种 Perl 方式而不是 Python。 -1 用于直接调用__ne__。比较两个值是一个比仅在其中一个上调用__eq____ne__ 复杂得多的过程。它可能在这里正常工作,因为您只是在比较数字,但在一般情况下这是不正确的并且是一个错误。【参考方案2】:

您可以使用列表推导:

def remove_values_from_list(the_list, val):
   return [value for value in the_list if value != val]

x = [1, 2, 3, 4, 2, 2, 3]
x = remove_values_from_list(x, 2)
print x
# [1, 3, 4, 3]

【讨论】:

如何在不检查的情况下删除项目? 这不会修改原始列表,而是返回一个新列表。 @Selinap:不,这是最佳选择,因为它只扫描列表一次。在您的原始代码中,in 运算符和 remove 方法都会扫描整个列表(直到找到匹配项),因此您最终会以这种方式多次扫描列表。 @mhawke, @John Y: 只需使用 x[:] = ... 而不是 x = 它将是“就地”而不是仅仅重新绑定名称“x”(速度是基本相同,而且比 x.remove 快得多!!!)。 我投了赞成票,因为在使用了 6 年的 Python 之后,我仍然不懂 Lambdas :)【参考方案3】:

如果必须修改原始列表,您可以使用切片分配,同时仍然使用有效的列表推导(或生成器表达式)。

>>> x = [1, 2, 3, 4, 2, 2, 3]
>>> x[:] = (value for value in x if value != 2)
>>> x
[1, 3, 4, 3]

【讨论】:

@Selinap:过滤器不修改列表,它返回一个新列表。 过滤器和列表推导不会修改列表。切片分配确实如此。和原来的例子一样。 我喜欢这个,因为它修改了 x 引用的列表。如果还有其他对该列表的引用,它们也会受到影响。这与 x = [ v for v in x if x != 2 ] 提案形成对比,后者创建一个新列表并更改 x 以引用它,而原始列表保持不变。【参考方案4】:

以更抽象的方式重复第一篇文章的解决方案:

>>> x = [1, 2, 3, 4, 2, 2, 3]
>>> while 2 in x: x.remove(2)
>>> x
[1, 3, 4, 3]

【讨论】:

不过是 O(n*n)。 @Hannes 不会是 O(n),因为它只经过一次循环并同时删除该项目? 考虑x = [1] * 10000 + [2] * 1000。循环体执行 1000 次,并且 .remove() 每次调用时都必须跳过 10000 个元素。对我来说,这闻起来像 O(n*n),但没有证据。我认为证明是假设列表中 2 的数量与其长度成正比。然后,该比例因子在大 O 表示法中消失了。不过,最好的情况是,列表中只有恒定数量的 2,不是 O(n^2),而是 O(2n),即 O(n)。【参考方案5】:

查看简单的解决方案

>>> [i for i in x if i != 2]

这将返回一个列表,其中包含 x 的所有元素,但不包含 2

【讨论】:

如果列表很长,可能需要一分钟才能完成,因为它会遍历所有项目。【参考方案6】:

列表理解的更好解决方案

x = [ i for i in x if i!=2 ]

【讨论】:

这是一个糟糕的解决方案,因为列表必须遍历 2*n 次才能出现 n 次 2。 不建议在您正在遍历的列表中添加或删除。恕我直言,不好的做法。【参考方案7】:

上述所有答案(除了 Martin Andersson 的答案)都会创建一个没有所需项目的新列表,而不是从原始列表中删除这些项目。

>>> import random, timeit
>>> a = list(range(5)) * 1000
>>> random.shuffle(a)

>>> b = a
>>> print(b is a)
True

>>> b = [x for x in b if x != 0]
>>> print(b is a)
False
>>> b.count(0)
0
>>> a.count(0)
1000

>>> b = a
>>> b = filter(lambda a: a != 2, x)
>>> print(b is a)
False

如果您有其他对列表的引用,这可能很重要。

要就地修改列表,请使用类似这样的方法

>>> def removeall_inplace(x, l):
...     for _ in xrange(l.count(x)):
...         l.remove(x)
...
>>> removeall_inplace(0, b)
>>> b is a
True
>>> a.count(0)
0

就速度而言,我笔记本电脑上的结果是(全部在 5000 个条目列表中,删除了 1000 个条目)

列表理解 - ~400us 过滤器 - ~900us .remove() 循环 - 50 毫秒

所以 .remove 循环大约慢了 100 倍......嗯,也许需要一种不同的方法。我发现最快的是使用列表推导,然后替换原始列表的内容。

>>> def removeall_replace(x, l):
....    t = [y for y in l if y != x]
....    del l[:]
....    l.extend(t)
removeall_replace() - 450us

【讨论】:

那为什么不在旧地址下重新分配新列表呢? def remove_all(x, l): return [y for y in l if y != x] 然后l = remove_all(3,l) @Dannid 这是第一个代码框中的第二种方法。它会创建一个新列表,而您不会修改旧列表。对列表的任何其他引用都将保持未过滤状态。 啊,对。我太忙于定义一个方法,我忽略了你已经完成的简单任务。【参考方案8】:

针对具有 1.000.000 个元素的列表/数组的 Numpy 方法和计时:

时间安排:

In [10]: a.shape
Out[10]: (1000000,)

In [13]: len(lst)
Out[13]: 1000000

In [18]: %timeit a[a != 2]
100 loops, best of 3: 2.94 ms per loop

In [19]: %timeit [x for x in lst if x != 2]
10 loops, best of 3: 79.7 ms per loop

结论: numpy 比列表理解方法快 27 倍(在我的笔记本上)

PS 如果你想将你的常规 Python 列表 lst 转换为 numpy 数组:

arr = np.array(lst)

设置:

import numpy as np
a = np.random.randint(0, 1000, 10**6)

In [10]: a.shape
Out[10]: (1000000,)

In [12]: lst = a.tolist()

In [13]: len(lst)
Out[13]: 1000000

检查:

In [14]: a[a != 2].shape
Out[14]: (998949,)

In [15]: len([x for x in lst if x != 2])
Out[15]: 998949

【讨论】:

【参考方案9】:

以可读性为代价,我认为这个版本会稍微快一些,因为它不会强制 while 重新检查列表,因此无论如何都要做与 remove 完全相同的工作:

x = [1, 2, 3, 4, 2, 2, 3]
def remove_values_from_list(the_list, val):
    for i in range(the_list.count(val)):
        the_list.remove(val)

remove_values_from_list(x, 2)

print(x)

【讨论】:

根据我的测量,对于您在代码中显示的列表,这种方法比列表理解方法(返回副本)慢约 36%。 很好,你注意到了。但是,因为我认为这可能会影响您的判断,所以我将我的版本与问题作者提出的第一个提案进行了比较。【参考方案10】:

要删除所有重复的匹配项并在列表中保留一个:

test = [1, 1, 2, 3]

newlist = list(set(test))

print newlist

[1, 2, 3]

这是我用于 Project Euler 的函数:

def removeOccurrences(e):
  return list(set(e))

【讨论】:

我需要在具有 250k 值的向量上执行此操作,它的工作原理就像一个魅力。 答案是:是的!而且我完全理解对于一个称职的程序员来说,如果拥有这么长的向量听起来完全是疯狂的。我以数学家的身份处理那里的问题,而不是担心优化解决方案,这可能会导致解决方案比标准更长。 (虽然我对超过 5 分钟的解决方案没有任何耐心。) 这将从列表中删除任何排序。 @JaredBurrows 可能是因为它没有回答目前的问题,而是一个完全不同的问题。 -1,这不是对 OP 问题的回答。是去除重复的解决方案,完全是另外一回事。【参考方案11】:
a = [1, 2, 2, 3, 1]
to_remove = 1
a = [i for i in a if i != to_remove]
print(a)

也许不是最pythonic,但对我来说仍然是最简单的哈哈

【讨论】:

【参考方案12】:

如果您不关心列表顺序,如果您确实关心最终顺序,我相信这可能比任何其他方式都快,存储原始索引并以此为依据。

category_ids.sort()
ones_last_index = category_ids.count('1')
del category_ids[0:ones_last_index]

【讨论】:

我知道你要去哪里,但是这段代码不起作用,因为你还需要起始索引,而不仅仅是 0。【参考方案13】:

>>> x = [1, 2, 3, 4, 2, 2, 3]

之前已经发布的最简单有效的解决方案是

>>> x[:] = [v for v in x if v != 2]
>>> x
[1, 3, 4, 3]

另一种应该使用较少内存但速度较慢的可能性是

>>> for i in range(len(x) - 1, -1, -1):
        if x[i] == 2:
            x.pop(i)  # takes time ~ len(x) - i
>>> x
[1, 3, 4, 3]

10% 匹配条目的长度为 1000 和 100000 的列表的计时结果:0.16 vs 0.25 ms,以及 23 vs 123 ms。

【讨论】:

【参考方案14】:
for i in range(a.count(' ')):
    a.remove(' ')

我相信要简单得多。

【讨论】:

请编辑您的答案以提高清晰度。请说明您推荐的代码的确切作用,它为什么起作用以及为什么这是您的推荐。还请正确格式化您的问题,以便从您的答案的其余部分清楚地辨别代码。【参考方案15】:

从 Python 列表中删除所有出现的值

lists = [6.9,7,8.9,3,5,4.9,1,2.9,7,9,12.9,10.9,11,7]
def remove_values_from_list():
    for list in lists:
      if(list!=7):
         print(list)
remove_values_from_list()

结果:6.9 8.9 3 5 4.9 1 2.9 9 12.9 10.9 11

或者,

lists = [6.9,7,8.9,3,5,4.9,1,2.9,7,9,12.9,10.9,11,7]
def remove_values_from_list(remove):
    for list in lists:
      if(list!=remove):
        print(list)
remove_values_from_list(7)

结果:6.9 8.9 3 5 4.9 1 2.9 9 12.9 10.9 11

【讨论】:

"Python '嵌套在每个 if 循环中'在一个函数中以 100% 的准确度工作!" 你不修改你只是打印元素的列表。将列表命名为列表也令人困惑【参考方案16】:

我只是为了一个列表而这样做的。我只是一个初学者。稍微高级一点的程序员肯定可以写出这样的函数。

for i in range(len(spam)):
    spam.remove('cat')
    if 'cat' not in spam:
         print('All instances of ' + 'cat ' + 'have been removed')
         break

【讨论】:

【参考方案17】:

如果您没有内置 filter 或者不想使用额外的空间并且您需要线性解决方案...

def remove_all(A, v):
    k = 0
    n = len(A)
    for i in range(n):
        if A[i] !=  v:
            A[k] = A[i]
            k += 1

    A = A[:k]

【讨论】:

【参考方案18】:
hello =  ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
#chech every item for a match
for item in range(len(hello)-1):
     if hello[item] == ' ': 
#if there is a match, rebuild the list with the list before the item + the list after the item
         hello = hello[:item] + hello [item + 1:]
print hello

['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd']

【讨论】:

请尝试详细说明您的答案。【参考方案19】:

我们也可以使用delpop 就地删除所有内容:

import random

def remove_values_from_list(lst, target):
    if type(lst) != list:
        return lst

    i = 0
    while i < len(lst):
        if lst[i] == target:
            lst.pop(i)  # length decreased by 1 already
        else:
            i += 1

    return lst

remove_values_from_list(None, 2)
remove_values_from_list([], 2)
remove_values_from_list([1, 2, 3, 4, 2, 2, 3], 2)
lst = remove_values_from_list([random.randrange(0, 10) for x in range(1000000)], 2)
print(len(lst))


现在为了效率:

In [21]: %timeit -n1 -r1 x = random.randrange(0,10)
1 loop, best of 1: 43.5 us per loop

In [22]: %timeit -n1 -r1 lst = [random.randrange(0, 10) for x in range(1000000)]
g1 loop, best of 1: 660 ms per loop

In [23]: %timeit -n1 -r1 lst = remove_values_from_list([random.randrange(0, 10) for x in range(1000000)]
    ...: , random.randrange(0,10))
1 loop, best of 1: 11.5 s per loop

In [27]: %timeit -n1 -r1 x = random.randrange(0,10); lst = [a for a in [random.randrange(0, 10) for x in
    ...:  range(1000000)] if x != a]
1 loop, best of 1: 710 ms per loop

正如我们所见,就地版本 remove_values_from_list() 不需要任何额外的内存,但它确实需要更多的时间来运行:

11 秒用于就地删除值 710 毫秒 用于列表推导,它在内存中分配一个新列表

【讨论】:

【参考方案20】:

没有人发布时间和空间复杂性的最佳答案,所以我想我会试一试。这是一个解决方案,它可以在不创建新数组的情况下以高效的时间复杂度删除所有出现的特定值。缺点是元素不保持顺序

时间复杂度:O(n) 额外的空间复杂度:O(1)

def main():
    test_case([1, 2, 3, 4, 2, 2, 3], 2)     # [1, 3, 3, 4]
    test_case([3, 3, 3], 3)                 # []
    test_case([1, 1, 1], 3)                 # [1, 1, 1]


def test_case(test_val, remove_val):
    remove_element_in_place(test_val, remove_val)
    print(test_val)


def remove_element_in_place(my_list, remove_value):
    length_my_list = len(my_list)
    swap_idx = length_my_list - 1

    for idx in range(length_my_list - 1, -1, -1):
        if my_list[idx] == remove_value:
            my_list[idx], my_list[swap_idx] = my_list[swap_idx], my_list[idx]
            swap_idx -= 1

    for pop_idx in range(length_my_list - swap_idx - 1):
        my_list.pop() # O(1) operation


if __name__ == '__main__':
    main()

【讨论】:

【参考方案21】:

关于速度!

import time
s_time = time.time()

print 'start'
a = range(100000000)
del a[:]
print 'finished in %0.2f' % (time.time() - s_time)
# start
# finished in 3.25

s_time = time.time()
print 'start'
a = range(100000000)
a = []
print 'finished in %0.2f' % (time.time() - s_time)
# start
# finished in 2.11

【讨论】:

【参考方案22】:
p=[2,3,4,4,4]
p.clear()
print(p)
[]

仅适用于 Python 3

【讨论】:

搞笑的是,这在所提问题的范围内并且是正确的。 我看不出它是如何正确的。这将从列表中删除所有项目,而不是所有出现的值【参考方案23】:

有什么问题:

Motor=['1','2','2']
For i in Motor:
       If i  != '2':
       Print(i)
Print(motor)

使用蟒蛇

【讨论】:

请解释您的代码行,以便其他用户了解其功能。谢谢!

以上是关于从列表中删除所有出现的值?的主要内容,如果未能解决你的问题,请参考以下文章

如何从列表 Dart 中删除重复项 |扑

点击 SwiftUI 时从列表中删除

审查(银)

如何从RabbitMQ删除所有队列

仅从向量列表中获取最常出现的值

如何访问 APEX 中选择列表控件的值?