基于每个列表的第一个元素,从列表中删除一个项目

6

给定:

a = [[1,2],[3,4],[5,6],[7,8]]
b = 3

我想要删除一个以b为第一项的a项目。所以在这个例子中,我们将删除[3,4],结果如下:

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

我的现有代码是:

if b in [i[0] for i in a]:
    pos = [i[0] for i in a].index(b)
       del a[pos]

这个方法可以实现,但速度较慢。有没有更好的方法?
编辑: 我之前没有测试过性能,所以可能做错了,但我得到了这个结果:
def fun1():
    lst = [[x, 2*x] for x in range(1000000)]
    lst = [x for x in lst if x[0] != 500]
    return lst

def fun2():
    lst = [[x, 2*x] for x in range(1000000)]
    for i in reversed(range(len(lst))):
        if lst[i][0] == 500:
            del lst[i]
    return lst

cProfile.runctx('fun1()', None, locals())
        6 function calls in 0.460 seconds

cProfile.runctx('fun2()', None, locals())
        6 function calls in 0.502 seconds

“相当大”有多大?足够大以至于值得考虑重新排列您的代码,使您拥有一个NumPy数组而不是列表吗?或者使用PyPy而不是CPython?或者使用Cython构建一个快速的C扩展,实现Coldspeed的解决方案? - abarnert
@abarnert 那对我来说有点超纲,但它并不是那么大。 - AndrewK
4个回答

11

就地修改并删除a:

for i in reversed(range(len(a))):
    if a[i][0] == 3:
        del a[i]

就地修改意味着它更加高效,因为它不会创建一个新的列表(像列表推导那样)。


由于OP请求一个高性能的解决方案,这里是两个在这里获得最高投票答案之间的timeit比较。

设置 -

a = np.random.choice(4, (100000, 2)).tolist()

print(a[:5])
[[2, 1], [2, 2], [3, 2], [3, 3], [3, 1]]
列表推导式 -

%timeit [x for x in a if x[0] != b]
11.1 ms ± 685 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

逆向删除 -

%%timeit
for i in reversed(range(len(a))):
    if a[i][0] == 3:
        del a[i]

10.1 ms ± 146 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

他们非常接近,但是反向删除在性能上占优势,因为它不需要像列表推导式那样在内存中生成一个新列表。


如果您不想构建一个新列表,那么a[:] = [x for x in a if x[0] != b]怎么样? - Matthias
@Matthias 注意,[x for x in a if x[0] != b] 创建了一个新的列表,并将其重新分配给 a - cs95
@cᴏʟᴅsᴘᴇᴇᴅ:花了我一些时间,但是我越看你的解决方案,就越喜欢这个想法。 - Matthias
1
@Matthias 我没有写列表推导式的答案是因为这是一个很棒的习惯用法,不太多的开发人员知道或使用,所以我想给它一些曝光。感谢 wim 很久以前向我介绍了这个方法。 - cs95
我比较了@KeyurPotdar和coldspeed,计时了你们两个的解决方案,但我发现列表推导式稍微快一些。我已经在我的帖子中编辑了我的结果。我不确定为什么它们与你的不同。 - AndrewK
1
@AndrewK 这取决于许多因素,其中之一是大小,另一个是Python版本。列表推导式被优化到实际上比循环本身稍微快一点的程度。反向删除在内存效率方面表现出色。另外,这是在Python3.6上计时的,你的结果可能会有所不同。 - cs95

7
您可以使用列表推导式:
```python ```
>>> a = [[1,2],[3,4],[5,6],[7,8]]
>>> b = 3
>>> a = [x for x in a if x[0] != b]
>>> a
[[1, 2], [5, 6], [7, 8]]

1
这里存在时空权衡 - 对于大型列表,我认为这个答案更可取,因为CS在Python的基于数组的列表上进行原地删除意味着将从删除位置到末尾的每个元素向下移动一个位置,如果列表很长并且有许多删除操作,我预计它会花费更长的时间。 - Russia Must Remove Putin

1
for i in a[:-1]:
    if i[0]==b:
        a.remove(i)

这是什么意思?

输出结果为

[[1, 2], [5, 6], [7, 8]]


你的意思是什么? - Pabasara Ranathunga
2
尝试使用 a = [[1,2],[3,4],[3,6],[7,8]] 进行测试。 - Matthias
我改了它。现在怎么样? :) - Pabasara Ranathunga
取消了踩,但请记住这会创建列表的副本,这是低效的,因为您无论如何都要修改原始列表。 - cs95
那么最好的方法是什么呢? :-) - Pabasara Ranathunga

1
如果您的列表很小,那么您也可以尝试使用过滤器。
a = [[1,2],[3,4],[5,6],[7,8]]
b = 3

print(list(filter(lambda x:x[0]!=b,a)))

输出:

[[1, 2], [5, 6], [7, 8]]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接