如何在迭代时从列表中删除项目?

Python

武藏

2020-05-28

我正在遍历Python中的元组列表,并尝试在满足特定条件的情况下将其删除。

for tup in somelist:
    if determine(tup):
         code_to_remove_tup

我应该用什么代替code_to_remove_tup我不知道如何以这种方式删除项目。

第4221篇《如何在迭代时从列表中删除项目?》来自Winter(https://github.com/aiyld/aiyld.github.io)的站点

20个回答
阿良 2020.05.28

我可以想到三种解决问题的方法。例如,我将创建一个随机的元组列表somelist = [(1,2,3), (4,5,6), (3,6,6), (7,8,9), (15,0,0), (10,11,12)]我选择的条件是sum of elements of a tuple = 15在最终列表中,我们将只有那些总和不等于15的元组。

我选择的是一个随机选择的示例。可以随意更改元组的列表条件,我选择了。

方法1.>使用您建议的框架(其中一个在for循环内填写代码)。我使用一个小的代码del来删除满足上述条件的元组。但是,如果两个连续放置的元组满足给定条件,则此方法将丢失一个元组(满足所述条件)。

for tup in somelist:
    if ( sum(tup)==15 ): 
        del somelist[somelist.index(tup)]

print somelist
>>> [(1, 2, 3), (3, 6, 6), (7, 8, 9), (10, 11, 12)]

方法2.>构造一个新列表,其中包含不满足给定条件的元素(元组)(这与删除满足给定条件的list的元素相同)。以下是该代码:

newlist1 = [somelist[tup] for tup in range(len(somelist)) if(sum(somelist[tup])!=15)]

print newlist1
>>>[(1, 2, 3), (7, 8, 9), (10, 11, 12)]

方法3.>查找满足给定条件的索引,然后使用与这些索引相对应的remove元素(元组)。以下是该代码。

indices = [i for i in range(len(somelist)) if(sum(somelist[i])==15)]
newlist2 = [tup for j, tup in enumerate(somelist) if j not in indices]

print newlist2
>>>[(1, 2, 3), (7, 8, 9), (10, 11, 12)]

方法1和方法2比方法3快方法2和方法3比方法1更有效。更喜欢method2对于上述示例,time(method1) : time(method2) : time(method3) = 1 : 1 : 1.7

Cathy 2020.05.28

提出一个数字列表,您想删除所有可被3整除的数字,

list_number =[i for i in range(100)]

使用list comprehension,这将创建一个新列表并创建新的内存空间

new_list =[i for i in list_number if i%3!=0]

使用lambda filter函数,这将创建结果新列表并占用内存空间

new_list = list(filter(lambda x:x%3!=0, list_number))

无需占用新列表和修改现有列表的存储空间

for index, value in enumerate(list_number):
    if list_number[index]%3==0:
        list_number.remove(value)
古一 2020.05.28

在某些情况下,您要做的不仅仅是一次过滤一个列表,还希望迭代时更改迭代。

这是一个示例,其中预先复制列表是不正确的,不可能进行反向迭代,并且列表理解也不是一种选择。

""" Sieve of Eratosthenes """

def generate_primes(n):
    """ Generates all primes less than n. """
    primes = list(range(2,n))
    idx = 0
    while idx < len(primes):
        p = primes[idx]
        for multiple in range(p+p, n, p):
            try:
                primes.remove(multiple)
            except ValueError:
                pass #EAFP
        idx += 1
        yield p
伽罗理查德 2020.05.28

如果以后要使用新列表,则只需将elem设置为None,然后在以后的循环中对其进行判断,如下所示

for i in li:
    i = None

for elem in li:
    if elem is None:
        continue

这样,您无需复制列表,而且更容易理解。

古一 2020.05.28

此类示例的最佳方法是列表理解

somelist = [tup for tup in somelist if determine(tup)]

如果您要做的事情比调用determine函数更复杂,我更喜欢构造一个新列表,然后随便添加它。例如

newlist = []
for tup in somelist:
    # lots of code here, possibly setting things up for calling determine
    if determine(tup):
        newlist.append(tup)
somelist = newlist

使用列表复制列表remove可能会使您的代码看起来更简洁,如以下答案之一所述。您绝对不应该对非常大的列表执行此操作,因为这涉及到首先复制整个列表,然后O(n) remove对要删除的每个元素执行操作,从而使其成为一种O(n^2)算法。

for tup in somelist[:]:
    # lots of code here, possibly setting things up for calling determine
    if determine(tup):
        newlist.append(tup)
猪猪 2020.05.28

对于具有很大潜力的任何事物,我使用以下内容。

import numpy as np

orig_list = np.array([1, 2, 3, 4, 5, 100, 8, 13])

remove_me = [100, 1]

cleaned = np.delete(orig_list, remove_me)
print(cleaned)

那应该比其他任何东西都快得多。

Tony凯 2020.05.28

如果要在迭代时从列表中删除元素,请使用while循环,以便可以在每次删除后更改当前索引和结束索引。

例:

i = 0
length = len(list1)

while i < length:
    if condition:
        list1.remove(list1[i])
        i -= 1
        length -= 1

    i += 1
Elena 2020.05.28

for循环将通过索引进行迭代。

认为你有一个清单,

[5, 7, 13, 29, 65, 91]

您使用了名为的列表变量lis并且您使用它删除。

你的变量

lis = [5, 7, 13, 29, 35, 65, 91]
       0  1   2   3   4   5   6

在第5次迭代中

您的数字35不是素数,因此您将其从列表中删除。

lis.remove(y)

然后下一个值(65)移至上一个索引。

lis = [5, 7, 13, 29, 65, 91]
       0  1   2   3   4   5

所以第4次迭代完成的指针移到了第5位。

那就是为什么您的循环自从移入上一个索引以来不覆盖65。

因此,您不应将列表引用到另一个仍引用原始而不是副本的变量中。

ite = lis #dont do it will reference instead copy

所以使用 list[::]

现在你会给,

[5, 7, 13, 29]

问题是您在迭代过程中从列表中删除了一个值,然后列表索引将崩溃。

因此您可以尝试理解。

它支持所有可迭代的对象,例如list,tuple,dict,string等

Mandy 2020.05.28

其他答案是正确的,因为从要迭代的列表中删除通常不是一个好主意。反向迭代避免了陷阱,但是遵循这样做的代码要困难得多,因此通常最好使用列表推导或filter

但是,在一种情况下,可以安全地从要迭代的序列中删除元素:如果仅在迭代时删除一个项目。可以使用a return或a 来确保break例如:

for i, item in enumerate(lst):
    if item % 4 == 0:
        foo(item)
        del lst[i]
        break

当您对满足条件的列表中的第一个项目执行副作用操作,然后立即从列表中删除该项目时,这通常比列表理解更容易理解。

西里神奇 2020.05.28

您可以尝试反向进行循环,因此对于some_list,您将执行以下操作:

list_len = len(some_list)
for i in range(list_len):
    reverse_i = list_len - 1 - i
    cur = some_list[reverse_i]

    # some logic with cur element

    if some_condition:
        some_list.pop(reverse_i)

这样,索引是对齐的,并且不会受到列表更新的影响(无论是否弹出cur元素)。

Tom凯 2020.05.28

最有效的方法是列表理解,很多人都表现出他们的情况,当然,这也是一个很好的方式得到一个iterator通过filter

Filter接收一个函数和一个序列。Filter将传递的函数依次应用于每个元素,然后根据函数返回值是True还是决定是保留还是丢弃该元素False

有一个例子(在元组中获得赔率):

list(filter(lambda x:x%2==1, (1, 2, 4, 5, 6, 9, 10, 15)))  
# result: [1, 5, 9, 15]

警告:您也不能处理迭代器。迭代器有时比序列更好。

飞羽 2020.05.28

这里的大多数答案都希望您创建列表的副本。我有一个用例,其中的列表很长(110K个项),而继续缩小列表会更明智。

首先,您需要将while循环替换为foreach循环

i = 0
while i < len(somelist):
    if determine(somelist[i]):
         del somelist[i]
    else:
        i += 1

iif块中的值不会更改,因为一旦删除了旧项,您将希望从同一索引中获取新项的值。

L木嘢 2020.05.28

如果当前列表项符合期望的条件,那么也只创建一个新列表可能很聪明。

所以:

for item in originalList:
   if (item != badValue):
        newList.append(item)

并且避免必须使用新的列表名称重新编码整个项目:

originalList[:] = newList

注意,来自Python文档:

copy.copy(x)返回x的浅表副本。

copy.deepcopy(x)返回x的深层副本。

十三 2020.05.28

您可能要使用filter()available作为内置函数。

欲了解更多详情,请点击这里

古一 2020.05.28

对于那些喜欢函数式编程的人:

somelist[:] = filter(lambda tup: not determine(tup), somelist)

要么

from itertools import ifilterfalse
somelist[:] = list(ifilterfalse(determine, somelist))
蛋蛋 2020.05.28

您需要获取列表的副本并首先对其进行迭代,否则迭代将失败,并可能导致意外结果。

例如(取决于列表的类型):

for tup in somelist[:]:
    etc....

一个例子:

>>> somelist = range(10)
>>> for x in somelist:
...     somelist.remove(x)
>>> somelist
[1, 3, 5, 7, 9]

>>> somelist = range(10)
>>> for x in somelist[:]:
...     somelist.remove(x)
>>> somelist
[]
斯丁 2020.05.28

您可以使用列表推导来创建一个仅包含您不想删除的元素的新列表:

somelist = [x for x in somelist if not determine(x)]

或者,通过分配给slice somelist[:],您可以将现有列表突变为仅包含所需的项目:

somelist[:] = [x for x in somelist if not determine(x)]

如果还有其他引用somelist需要反映更改,则此方法可能很有用

除了理解之外,您还可以使用itertools在Python 2中:

from itertools import ifilterfalse
somelist[:] = ifilterfalse(determine, somelist)

或在Python 3中:

from itertools import filterfalse
somelist[:] = filterfalse(determine, somelist)

为了清楚起见,以及对于那些发现使用[:]黑变或模糊表示法的人,这里有一个更明确的选择。从理论上讲,它在空间和时间上的表现应该与上面的单层表现相同。

temp = []
while somelist:
    x = somelist.pop()
    if not determine(x):
        temp.append(x)
while temp:
    somelist.append(templist.pop())

它也可以在其他语言中工作,而这些语言可能不具有Python列表替换项功能,并且只需进行很少的修改即可。例如,并非所有语言都False像Python一样将空列表转换为。您可以替换while somelist:更明确的内容,例如while len(somelist) > 0:

王者一打九 2020.05.28

我需要使用大量列表来完成此操作,并且复制列表似乎很昂贵,尤其是因为在我的情况下,与保留的项目相比,删除的数量很少。我采用了这种低级方法。

array = [lots of stuff]
arraySize = len(array)
i = 0
while i < arraySize:
    if someTest(array[i]):
        del array[i]
        arraySize -= 1
    else:
        i += 1

我不知道几个删除相比复制大型列表的效率如何。如果您有任何见解,请发表评论。

神无 2020.05.28

暗示列表理解的答案几乎是正确的-除了它们会建立一个全新的列表,然后为其命名与旧列表相同,而不会在原地修改旧列表。这与@Lennart的建议中的选择性删除操作不同-速度更快,但是如果通过多个引用访问列表,则事实是您只是在重新放置其中一个引用而不更改列表对象本身可能会导致微妙的灾难性错误。

幸运的是,获得列表理解的速度和就地变更所需的语义非常容易-只需编写代码:

somelist[:] = [tup for tup in somelist if determine(tup)]

请注意与其他答案的细微差别:这不是分配给裸名-而是分配给恰好是整个列表的列表切片,从而替换同一Python列表对象中的列表内容 ,而不仅仅是重新放置一个引用(从先前的列表对象到新的列表对象),就像其他答案一样。

Mandy村村 2020.05.28
for i in range(len(somelist) - 1, -1, -1):
    if some_condition(somelist, i):
        del somelist[i]

您需要向后走,否则就像将您坐在的树枝锯掉一样:-)

Python 2用户:替换rangexrange以避免创建硬编码列表

问题类别

JavaScript Ckeditor Python Webpack TypeScript Vue.js React.js ExpressJS KoaJS CSS Node.js HTML Django 单元测试 PHP Asp.net jQuery Bootstrap IOS Android