一次性删除多个数组中所有给定值的出现

3

我有四个数组,分别是A、B、C和D,大小均为NumElements,我想从它们中删除所有的0。如果A中有一个零,那么B、C和D在同一位置也会有一个零。所以我考虑循环遍历A的元素:

for n in range(NumElements):
    if A[n]==0:
       A.pop(n)
       B.pop(n)
       C.pop(n)
       D.pop(n)

当然,这样做是不行的,因为从数组中弹出0会减小它们的大小,所以我最终会尝试访问A [NumElements-1],而现在A只有NumElements-m长。我知道应该使用数组副本进行操作,但数组非常长,我想保持内存消耗低,因为我正在Java虚拟机中工作(别问我为什么:()。此外,我希望采用高效但最重要的可读性方法(这段代码必须由像我这样的Python文盲来维护,所以我需要简化)。
6个回答

5
a,b,c,d = [filter(lambda i: i != 0, l) for l in [a,b,c,d]]

过滤每个列表,删除不为0的元素。

编辑,

只是解释一下正在发生的事情

过滤器接受一个表达式,并通过将函数应用于列表中的所有内容来“过滤”列表,即返回False的所有内容。

Lambda是函数的简写

所以

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

def is_even(x):
    return x % 2 == 0
filter(is_even, a)

非常紧凑!我不认为我会选择这个解决方案,因为我不知道这些“filter”和“lambda”指令,而且维护者们可能知道得更少。但它仍然令人印象深刻!展示了Python的强大之处。 - DeltaIV

4
如果它们在同一位置都有零,则反向循环索引并从每个列表中删除该索引:
for i in reversed(range(NumElements)):
    if not A[i]:
        del A[i], B[i], C[i], D[i]

通过反向循环列表,您可以保持索引稳定(只有当前索引之后的元素被移除,仅缩小列表尾部)。由于您没有使用list.pop()的返回值(您得到的只是0,对吧?),因此最好直接使用列表索引的del
这里我使用了reversed(range(NumElements))而不是计算更费力的range(NumElements - 1, -1, -1);它同样有效,但更易读。reversed()函数返回一个迭代器,非常高效地处理反转数字序列。在Python 2中,您可以使用xrange()完成相同的操作:
for i in reversed(xrange(NumElements)):

演示:

>>> A = [1, 2, 0, 4, 5, 0]
>>> B = [2, 4, 0, 10, 9, 0]
>>> C = [5, 3, 0, 10, 8, 0]
>>> D = [10, 3, 0, 1, 34, 0]
>>> for i in reversed(range(NumElements)):
...     if not A[i]:
...         del A[i], B[i], C[i], D[i]
... 
>>> A, B, C, D
([1, 2, 4, 5], [2, 4, 10, 9], [5, 3, 10, 8], [10, 3, 1, 34])

+1,不错,我忘记了 ::-1,在工作中使用的是更多的C#而不是Python :) - Roman Pekar
1
@RomanPekar:我放弃了创建新列表的想法;在这种情况下,我更喜欢使用reversed(range()) - Martijn Pieters
是的,我明白了,这对我来说无论如何都是一个好的提醒。 - Roman Pekar
非常感谢!这个代码简单易懂,没有复制数组,并且解释得非常详细。此外,我还学到了del和xrange的使用方法,这是额外的福利。 - DeltaIV
哦,顺便说一下,我认为 if not elem: 是错误的,对吧?应该是 if not A[i]: 吧? - DeltaIV

2

我认为你可以像这样做。我不知道这是否足够符合Python语言的风格。

A = [1, 2, 4, 0]
B = [6, 0, 4, 3, 9]
C = [12, 5, 32, 0, 90]

for row in [A, B, C]:
    for i, v in enumerate(row):
        if v == 0: del row[i]

或者,如果你确定所有列表中索引为零的值都相等:

for i in range(len(A) - 1, -1, -1):
    if A[i] == 0:
        for row in [A, B, C]:
             del row[i]   

不错!但是我不明白为什么第一个形式有效。我猜测这个enumerate函数创建了一个“静态”的索引列表,从0到NumElements-1。所以,假设我删除了至少一个元素,当我尝试访问A[NumElements-1]时,应该会出现错误。对吗? - DeltaIV
抱歉,我刚刚发现第一个解决方案对于多个0不起作用。你可以使用for i, v in reversed(list(enumerate(A))):,但是使用range的解决方案更好。 - Roman Pekar

2
从另一个角度开始工作!
for n in range(NumElements-1,-1,-1):
    if A[n]==0:
       A.pop(n)
       B.pop(n)
       C.pop(n)
       D.pop(n)

1
这可能是一种黑客方法,但它很简单且有效。
>>> a = [1,2,3]
>>> b = [1,10,99]
>>> c = [1,87,22]
>>> d = []
>>> d.extend([a,b,c])
>>> to_remove = 1
>>> [i.remove(to_remove) for i in d]
>>> d
[[2, 3], [10, 99], [87, 22]]

请注意,这将删除所有标记为to_remove的元素,而不仅仅是开头的零。我假设这对您来说是可以接受的,因为您说您想要删除所有的零。

这不会删除所有标记为 to_remove 的元素,它只会删除第一个。 例如,尝试 a = [1,1,2,3]; b = [1,1,2,3]; c = [1,1,2,3] - DSM

1

请看我的另一个答案 Python中的列表访问。您可以遍历列表A并将0的索引存储在临时列表中,然后将它们弹出。


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