localizando e substituindo elementos em uma lista

273

Eu tenho que pesquisar uma lista e substituir todas as ocorrências de um elemento por outro. Até agora, minhas tentativas de código não me levaram a lugar algum, qual é a melhor maneira de fazer isso?

Por exemplo, suponha que minha lista tenha os seguintes números inteiros

>>> a = [1,2,3,4,5,1,2,3,4,5,1]

e preciso substituir todas as ocorrências do número 1 pelo valor 10, para que a saída que eu preciso seja

>>> a = [10, 2, 3, 4, 5, 10, 2, 3, 4, 5, 10]

Assim, meu objetivo é substituir todas as instâncias do número 1 pelo número 10.

James
fonte
11
O que é isso, a propósito?
outis 6/04/10
Duplicado de stackoverflow.com/q/1540049/819417
Cees Timmerman

Respostas:

250
>>> a= [1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1]
>>> for n, i in enumerate(a):
...   if i == 1:
...      a[n] = 10
...
>>> a
[10, 2, 3, 4, 5, 10, 2, 3, 4, 5, 10]
ghostdog74
fonte
15
Esta é uma solução ruim e muito não-pitônica. Considere usar a compreensão da lista.
AdHominem 31/12/16
204
Essa é uma solução ótima, embora não muito pitônica. Considere usar a compreensão da lista.
Jean-François Corbett
Considere usar a compreensão da lista, como é feito por @outis abaixo!
AMC
6
Isso funciona melhor do que a compreensão da lista, não é? Ele faz atualizações no local em vez de gerar uma nova lista.
Neverendingqs
@neverendingqs: Não. A sobrecarga de intérpretes domina a operação e a compreensão é menor. A compreensão apresenta um desempenho um pouco melhor, especialmente com uma proporção maior de elementos que passam na condição de substituição. Tenha alguns horários: ideone.com/ZrCy6z
user2357112 suporta Monica
519

Tente usar uma compreensão de lista e o operador ternário .

>>> a=[1,2,3,1,3,2,1,1]
>>> [4 if x==1 else x for x in a]
[4, 2, 3, 4, 3, 2, 4, 4]
fora
fonte
9
Mas isso não muda, acerto? Acho OP queria amudar
Dula
10
@Dula você pode fazer a = [4 if x == 1 pessoa x for x in a], isso afetará a
Alekhya Vemavarapu
@ Dula: a pergunta é vaga sobre se adeve sofrer uma mutação, mas (como mostra Alekhya) é trivial lidar com ambos os casos ao usar uma lista de compreensão.
Outis
34
Se você deseja fazer a mutação a, deve fazê-lo a[:] = [4 if x==1 else x for x in a](observe a fatia da lista completa). Basta fazer o a =vai criar uma nova lista acom um diferente id()(identidade) do original
Chris_Rands
39

A compreensão da lista funciona bem e o looping com o enumerate pode economizar um pouco de memória (porque basicamente a operação está sendo executada no local).

Há também programação funcional. Veja o uso do mapa :

>>> a = [1,2,3,2,3,4,3,5,6,6,5,4,5,4,3,4,3,2,1]
>>> map(lambda x: x if x != 4 else 'sss', a)
[1, 2, 3, 2, 3, 'sss', 3, 5, 6, 6, 5, 'sss', 5, 'sss', 3, 'sss', 3, 2, 1]
damzam
fonte
17
+1. É muito ruim lambdae mapé considerado antitônico.
Out
4
Não tenho certeza de que o lambda ou o mapa sejam inerentemente antitônicos, mas concordo que a compreensão da lista é mais limpa e legível do que usar os dois em conjunto.
damzam
7
Eu não os considero antitônicos, mas muitos o consideram, incluindo Guido van Rossum ( artima.com/weblogs/viewpost.jsp?thread=98196 ). É uma daquelas coisas sectárias.
outis 8/04/10
36

Se você tiver vários valores para substituir, também poderá usar um dicionário:

a = [1, 2, 3, 4, 1, 5, 3, 2, 6, 1, 1]
dic = {1:10, 2:20, 3:'foo'}

print([dic.get(n, n) for n in a])

> [10, 20, 'foo', 4, 10, 5, 'foo', 20, 6, 10, 10]
roipoussiere
fonte
1
Isso não gera um erro se nnão for encontrado dic?
21416 Neil A.
3
@ user2914540 Melhorei um pouco sua resposta para que funcione se nnão for encontrada. Espero que você não se importe. Sua try/exceptsolução não foi boa.
Jrjc
Ah, sim, está melhor.
roipoussiere
1
@jrjc @roipoussiere para substituições no local, try-excepté pelo menos 50% mais rápido! Dê uma olhada nesta resposta
lifebalance 23/11
4
if n in dic.keys()é ruim em termos de desempenho. Use if n in dicor dic.get(n,n)(valor padrão)
Jean-François Fabre
12
>>> a=[1,2,3,4,5,1,2,3,4,5,1]
>>> item_to_replace = 1
>>> replacement_value = 6
>>> indices_to_replace = [i for i,x in enumerate(a) if x==item_to_replace]
>>> indices_to_replace
[0, 5, 10]
>>> for i in indices_to_replace:
...     a[i] = replacement_value
... 
>>> a
[6, 2, 3, 4, 5, 6, 2, 3, 4, 5, 6]
>>> 
John La Rooy
fonte
Método médio rápido, mas muito sensível. Por favor, veja horários na minha resposta.
dawg
10
a = [1,2,3,4,5,1,2,3,4,5,1,12]
for i in range (len(a)):
    if a[i]==2:
        a[i]=123

Você pode usar um loop for e ou while; no entanto, se você conhece a função Enumerar interna, é recomendável usar Enumerar. 1

Eimal Dorani
fonte
1
Essa é a única maneira sã (legível) de fazer isso quando você precisar executar operações mais complexas nos itens da lista. Por exemplo, se cada item da lista for uma cadeia longa que precise de algum tipo de pesquisa e substituição.
Not2qubit #
8

Para substituir facilmente todos 1com 10em a = [1,2,3,4,5,1,2,3,4,5,1]um poderia usar a combinação lambda + mapa de uma linha seguinte, e 'Olha, Ma, há FI ou Fors!' :

# This substitutes all '1' with '10' in list 'a' and places result in list 'c':

c = list(map(lambda b: b.replace("1","10"), a))

J.Paul
fonte
Método mais lento de longe. Você está chamando um lambdaelemento em cada lista ...
dawg
4

A seguir, é um método muito direto no Python 2.x

 a = [1,2,3,4,5,1,2,3,4,5,1]        #Replacing every 1 with 10
 for i in xrange(len(a)):
   if a[i] == 1:
     a[i] = 10  
 print a

Este método funciona. Comentários são bem-vindos. Espero que ajude :)

Além disso, tente entender como Outis do e do damzam soluções trabalho. As compressões de lista e a função lambda são ferramentas úteis.

Ananay Mital
fonte
4

Em listas longas e ocorrências raras, o uso é cerca de 3x mais rápido list.index()- comparado aos métodos de iteração de etapa única apresentados nas outras respostas.

def list_replace(lst, old=1, new=10):
    """replace list elements (inplace)"""
    i = -1
    try:
        while 1:
            i = lst.index(old, i + 1)
            lst[i] = new
    except ValueError:
        pass
kxr
fonte
Este é o método mais rápido que encontrei. Por favor, veja horários na minha resposta. Ótimo!
dawg
3

Eu sei que essa é uma pergunta muito antiga e há inúmeras maneiras de fazer isso. O mais simples que encontrei está usando o numpypacote.

import numpy

arr = numpy.asarray([1, 6, 1, 9, 8])
arr[ arr == 8 ] = 0 # change all occurrences of 8 by 0
print(arr)
Tiago Vieira
fonte
3

Meu caso de usuário foi substituído Nonepor algum valor padrão.

Eu cronometrei abordagens para esse problema que foram apresentadas aqui, incluindo a do @kxr - using str.count.

Código de teste no ipython com Python 3.8.1:

def rep1(lst, replacer = 0):
    ''' List comprehension, new list '''

    return [item if item is not None else replacer for item in lst]


def rep2(lst, replacer = 0):
    ''' List comprehension, in-place '''    
    lst[:] =  [item if item is not None else replacer for item in lst]

    return lst


def rep3(lst, replacer = 0):
    ''' enumerate() with comparison - in-place '''
    for idx, item in enumerate(lst):
        if item is None:
            lst[idx] = replacer

    return lst


def rep4(lst, replacer = 0):
    ''' Using str.index + Exception, in-place '''

    idx = -1
    # none_amount = lst.count(None)
    while True:
        try:
            idx = lst.index(None, idx+1)
        except ValueError:
            break
        else:
            lst[idx] = replacer

    return lst


def rep5(lst, replacer = 0):
    ''' Using str.index + str.count, in-place '''

    idx = -1
    for _ in range(lst.count(None)):
        idx = lst.index(None, idx+1)
        lst[idx] = replacer

    return lst


def rep6(lst, replacer = 0):
    ''' Using map, return map iterator '''

    return map(lambda item: item if item is not None else replacer, lst)


def rep7(lst, replacer = 0):
    ''' Using map, return new list '''

    return list(map(lambda item: item if item is not None else replacer, lst))


lst = [5]*10**6
# lst = [None]*10**6

%timeit rep1(lst)    
%timeit rep2(lst)    
%timeit rep3(lst)    
%timeit rep4(lst)    
%timeit rep5(lst)    
%timeit rep6(lst)    
%timeit rep7(lst)    

Eu recebo:

26.3 ms ± 163 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
29.3 ms ± 206 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
33.8 ms ± 191 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
11.9 ms ± 37.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
11.9 ms ± 60.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
260 ns ± 1.84 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
56.5 ms ± 204 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Usar o interno str.indexé de fato mais rápido do que qualquer comparação manual.

Eu não sabia se a exceção no teste 4 seria mais trabalhosa do que em usar str.count, a diferença parece insignificante.

Observe que map()(teste 6) retorna um iterador e não uma lista real, portanto, teste 7.

Jay
fonte
2

Você pode simplesmente usar a compreensão da lista em python:

def replace_element(YOUR_LIST, set_to=NEW_VALUE):
    return [i
            if SOME_CONDITION
            else NEW_VALUE
            for i in YOUR_LIST]

para o seu caso, onde você deseja substituir todas as ocorrências de 1 por 10, o snippet de código será assim:

def replace_element(YOUR_LIST, set_to=10):
    return [i
            if i != 1  # keeps all elements not equal to one
            else set_to  # replaces 1 with 10
            for i in YOUR_LIST]
bassel7
fonte
3
Embora esse trecho de código possa resolver a questão, incluir uma explicação realmente ajuda a melhorar a qualidade da sua postagem. Lembre-se de que você está respondendo à pergunta dos leitores no futuro e essas pessoas podem não saber os motivos da sua sugestão de código. Tente também não sobrecarregar seu código com comentários explicativos, pois isso reduz a legibilidade do código e das explicações!
Filnor
-1

Encontre e substitua apenas um item

ur_list = [1,2,1]     # replace the first 1 wiz 11

loc = ur_list.index(1)
ur_list.remove(1)
ur_list.insert(loc, 11)

----------
[11,2,1]
bereket gebredingle
fonte