Remover todas as ocorrências de um valor de uma lista?


378

No Python remove(), a primeira ocorrência de valor será removida de uma lista.

Como remover todas as ocorrências de um valor de uma lista?

Isto é o que eu tenho em mente:

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

Respostas:


506

Abordagem funcional:

Python 3.x

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

ou

>>> 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]

120
Use a compreensão da lista sobre o filtro + lambda; o primeiro é mais legível, além de geralmente mais eficiente.
21909 habnabit

17
s / geralmente / geralmente sendo /
habnabit

99
O código para olhares sugestão de habnabit como este:[y for y in x if y != 2]
coredumperror

8
Eu não chamaria essa solução de melhor. As compreensões de lista são mais rápidas e fáceis de entender ao percorrer o código. Isso preferiria mais uma maneira Perl do que Python.
Peter Nimroot

3
-1 para chamar diretamente __ne__. Comparar dois valores é um processo muito mais complexo do que apenas chamar __eq__ou __ne__em um deles. Pode funcionar corretamente aqui porque você está apenas comparando números, mas no caso geral isso é incorreto e é um bug.
Aran-Fey

212

Você pode usar uma compreensão de lista:

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]

7
Como você removeria itens sem verificá-los?
21711 Alexander Ljungberg

18
Isso não modifica a lista original, mas retorna uma nova lista.
John Y

6
@Selinap: Não, isso é ideal, pois verifica a lista apenas uma vez. No código original, tanto o inoperador quanto o removemétodo digitalizam a lista inteira (até encontrarem uma correspondência), para que você acabe digitalizando a lista várias vezes dessa maneira.
John Kugelman

4
@mhawke, @John Y: basta usar x [:] = ... em vez de x = e ele estará "no local", em vez de apenas alterar o nome 'x' (a velocidade é essencialmente a mesma e MUITO mais rápida que x .remove pode ser !!!).
287 Alex Martelli

10
Eu voto isto porque depois de 6 anos de Python Eu ainda não entendo Lambdas :)
Benjamin

107

Você pode usar a atribuição de fatia se a lista original precisar ser modificada, enquanto ainda estiver usando uma compreensão eficiente da lista (ou expressão do gerador).

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

11
@Selinap: o filtro não modifica a lista, retorna uma nova lista.
EM

as compreensões de filtro e lista não modificam uma lista. atribuição de fatia faz. e o exemplo original sim.
A.Coady

7
Eu gosto disso porque modifica a lista a que x se refere. Se houver outras referências a essa lista, elas também serão afetadas. Isso contrasta com as x = [ v for v in x if x != 2 ]propostas, que criam uma nova lista e alteram x para se referir a ela, deixando a lista original intocada.
Hannes

40

Repetindo a solução do primeiro post de uma maneira mais abstrata:

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

19
É O (n * n), no entanto.
Hannes

@ Hannes não seria O (n), uma vez que está passando pelo loop apenas uma vez e ao mesmo tempo removendo o item?
penta

11
Considere x = [1] * 10000 + [2] * 1000. O corpo do loop é executado 1000 vezes e .remove () precisa pular 10000 elementos toda vez que é chamado. Isso cheira a O (n * n) para mim, mas não é prova. Penso que a prova seria assumir que o número de 2s na lista é proporcional ao seu comprimento. Esse fator de proporcionalidade desaparece na notação O-grande. O melhor caso, porém, de apenas um número constante de 2s na lista, não é O (n ^ 2), apenas O (2n) que é O (n).
Hannes

23

Veja a solução simples

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

Isso retornará uma lista com todos os elementos xsem2


11

Todas as respostas acima (exceto as de Martin Andersson) criam uma nova lista sem os itens desejados, em vez de removê-los da lista original.

>>> 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

Isso pode ser importante se você tiver outras referências à lista por aí.

Para modificar a lista, use um método como este

>>> 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

Quanto à velocidade, os resultados no meu laptop são (todos em uma lista de 5000 entradas com 1000 entradas removidas)

  • Compreensão da lista - ~ 400us
  • Filtro - ~ 900us
  • loop .remove () - 50ms

Portanto, o loop .remove é cerca de 100x mais lento ........ Hmmm, talvez seja necessária uma abordagem diferente. O mais rápido que encontrei foi usar a compreensão da lista, mas depois substituir o conteúdo da lista original.

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

Por que não reatribuir a nova lista com o endereço antigo então? def remove_all(x, l): return [y for y in l if y != x]entãol = remove_all(3,l)
Dannid

@ Dannid Esse é o segundo método na primeira caixa de código. Ele cria uma nova lista e você não está modificando a lista antiga. Quaisquer outras referências à lista permanecerão sem filtro.
Paul S

Ah, certo. Fiquei tão envolvido na definição de um método que negligenciei a tarefa simples que você já havia feito.
Dannid

7

você consegue fazer isso

while 2 in x:   
    x.remove(2)

3
Isso é uma má solução, uma vez que a lista tem que ser percorrido 2 * n vezes para n ocorrências de 2.
cxxl

Não é recomendável adicionar ou remover da lista que você está percorrendo. Má prática IMHO.
Aman Mathur

5

À custa da legibilidade, acho que esta versão é um pouco mais rápida, pois não força o tempo a reexaminar a lista, fazendo exatamente o mesmo trabalho que a remoção precisa fazer:

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)

Para a lista que você mostra em seu código, essa abordagem é cerca de 36% mais lenta que o método de compreensão de lista (que retorna uma cópia), de acordo com minha medição.
perfil completo de djsmith

Bom você percebeu isso. No entanto, porque acho que isso pode ter escapado ao seu julgamento, eu estava comparando minha versão com a primeira proposta feita pelo autor da pergunta.
Martin Andersson

4

Abordagem numpy e tempos em relação a uma lista / matriz com 1.000.000 elementos:

Horários:

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

Conclusão: numpy é 27 vezes mais rápido (no meu notebook) em comparação com a abordagem de compreensão de lista

PS, se você deseja converter sua lista regular de Python lstem um array numpy:

arr = np.array(lst)

Configuração:

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

Verifica:

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

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

4
a = [1, 2, 2, 3, 1]
to_remove = 1
a = [i for i in a if i != to_remove]
print(a)

Talvez não seja o mais pitonico, mas ainda o mais fácil para mim haha


3

Para remover todas as ocorrências duplicadas e deixar uma na lista:

test = [1, 1, 2, 3]

newlist = list(set(test))

print newlist

[1, 2, 3]

Aqui está a função que eu usei para o Project Euler:

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

2
Eu precisava fazer isso em um vetor com valores de 250k, e funciona como um encanto.
rschwieb

11
A resposta é sim! E eu entendo completamente se ter um vetor que pareça muito louco para um programador competente. Abordo os problemas lá como matemático, sem me preocupar com a otimização das soluções, e isso pode levar a soluções mais longas do que o par. (Embora eu não tenho nenhuma paciência para soluções de mais de 5 minutos.)
rschwieb

6
Isso removerá qualquer pedido da lista.
asmeurer

4
@JaredBurrows talvez porque não responda à pergunta como está atualmente, mas uma pergunta bem diferente.
drevicko

6
-1, essa não é uma resposta para a pergunta do OP. É uma solução para remover duplicatas, o que é uma questão completamente diferente.
Anoyz 22/07

2

Acredito que provavelmente seja mais rápido do que qualquer outra maneira, se você não se importa com a ordem das listas, se você se importa com a ordem final, armazene os índices do original e recorra a ele.

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

2
Eu entendo de onde your'e indo, mas este código não funcionará desde que você precisa também o índice de início e não apenas 0.
Shedokan

2
for i in range(a.count(' ')):
    a.remove(' ')

Muito mais simples, acredito.


2
edite sua resposta para melhorar a clareza. Deixe claro o que exatamente o seu código recomendado faz, por que funciona e por que essa é sua recomendação. Formate corretamente sua pergunta para que o código seja claramente discernível do restante da sua resposta.
Ortund 3/16

2

Deixei

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

A solução mais simples e eficiente, como já publicada anteriormente, é

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

Outra possibilidade que deveria usar menos memória mas ser mais lenta é

>>> 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]

Resultados de tempo para listas de comprimento 1000 e 100000 com 10% de entradas correspondentes: 0,16 vs 0,25 ms e 23 vs 123 ms.

Timing com comprimento 1000

Temporização com comprimento 100000


1

Remova todas as ocorrências de um valor de uma lista 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()

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

Alternativamente,

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)

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


"Python 'aninhado para cada loop se' dentro de uma função que trabalha com 100% de precisão!"
Rafiqul786

Você não modifica a lista, apenas imprime os elementos. Também nomear uma lista de listas é confuso
psych kon

0

Se você não possui um built-in filterou não deseja usar espaço extra e precisa de uma solução linear ...

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]

0
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

['Olá Mundo']


por favor, tente elaborar sua resposta com explicações.
parlad

0

Eu apenas fiz isso para uma lista. Eu sou apenas um iniciante. Um programador um pouco mais avançado certamente pode escrever uma função como esta.

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

0

Também podemos remover todos os locais usando delou pop:

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))

Agora, para a eficiência:

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

Como vemos, a versão in-loco remove_values_from_list()não requer memória extra, mas leva muito mais tempo para ser executada:

  • 11 segundos para valores de remoção no local
  • 710 milli segundos para compreensão de lista, que aloca uma nova lista na memória

0

Ninguém postou uma resposta ótima para a complexidade de tempo e espaço, então pensei em dar uma chance. Aqui está uma solução que remove todas as ocorrências de um valor específico sem criar uma nova matriz e com uma complexidade de tempo eficiente. A desvantagem é que os elementos não mantêm a ordem .

Complexidade temporal: O (n)
Complexidade adicional de espaço: 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()

-1

Sobre a velocidade!

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

-3
p=[2,3,4,4,4]
p.clear()
print(p)
[]

Somente com Python 3


2
Hilariamente, isso está dentro do escopo da pergunta e está correto.
28517 Erich

Não vejo como está correto. Isso removerá todos os itens da lista, nem todas as ocorrências de um valor .
Georgy

-3

O que há de errado com:

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

Usando anaconda


2
Por favor, explique suas linhas de código para que outros usuários possam entender sua funcionalidade. Obrigado!
Ignacio Ara

Este código não removerá nada da lista.
Georgy
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.