поиск и замена элементов в списке

273

Я должен искать в списке и заменить все вхождения одного элемента другим. Пока мои попытки в коде ни к чему не привели, как лучше всего это сделать?

Например, предположим, что мой список имеет следующие целые числа

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

и мне нужно заменить все вхождения числа 1 значением 10, поэтому мне нужен выход

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

Таким образом, моя цель - заменить все экземпляры числа 1 числом 10.

Джеймс
источник
11
Кстати, для чего это?
2010 г.
Дубликат stackoverflow.com/q/1540049/819417
Сиз Тиммерман

Ответы:

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
источник
15
Это плохое и очень непитонное решение. Подумайте об использовании списка понимания.
AdHominem
205
Это прекрасное, но очень непитонное решение. Подумайте об использовании списка понимания.
Жан-Франсуа Корбетт
Подумайте об использовании списка, например, @outis ниже!
AMC
6
Это работает лучше, чем понимание списка, не так ли? Он производит обновления на месте вместо создания нового списка.
neverendingqs
@neverendingqs: Нет. Работа над переводчиком доминирует над операцией, и понимание имеет меньше. Понимание работает немного лучше, особенно с большей долей элементов, проходящих условие замены. Имейте некоторое время: ideone.com/ZrCy6z
user2357112 поддерживает Monica
519

Попробуйте использовать понимание списка и троичный оператор .

>>> 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]
outis
источник
9
Но это не изменится, aправда? Я думаю, что OP хотел aизмениться
Dula
10
@Dula, вы можете сделать a = [4, если x == 1, иначе x для x в a], это повлияет на a
Alekhya Vemavarapu
@Dula: вопрос неясен относительно того, aдолжен ли он изменяться, но (как показывает Алехья) тривиально обрабатывать любой случай при использовании понимания списка.
outis
34
Если вы хотите изменить, aто вам следует это сделать a[:] = [4 if x==1 else x for x in a](обратите внимание на полный список фрагментов). Просто сделав a =, создаст новый список aс другим id()(личность) от оригинала
Chris_Rands
39

Понимание списков работает хорошо, и циклическое выполнение с помощью перечисления может сэкономить вам немного памяти (поскольку операция по существу выполняется на месте).

Там также функциональное программирование. Смотрите использование карты :

>>> 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
источник
17
+1. Это очень плохо lambdaи mapсчитается непитонным.
outis
4
Я не уверен, что лямбда или карта по своей сути непифоничны, но я согласен, что понимание списка чище и более читабельно, чем использование двух из них в сочетании.
Дамзам
7
Я сам не считаю их непифоническими, но многие это делают, включая Гвидо ван Россума ( artima.com/weblogs/viewpost.jsp?thread=98196 ). Это одна из тех сектантских вещей.
2010 г.
36

Если вам нужно заменить несколько значений, вы также можете использовать словарь:

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
источник
1
Разве это не выдает ошибку, если nне найден в dic?
Нил А.
3
@ user2914540 Я немного улучшил ваш ответ, чтобы он работал, если nне найден. Надеюсь, ты не против. Ваше try/exceptрешение не было хорошим.
jrjc
Ах да, так лучше.
roipoussiere
1
@jrjc @roipoussiere для замены на месте, как try-exceptминимум на 50% быстрее! Взгляните на этот ответ
lifebalance
4
if n in dic.keys()плохая производительность. Использовать if n in dicили dic.get(n,n)(значение по умолчанию)
Жан-Франсуа Фабр
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]
>>> 
Джон Ла Рой
источник
Средне быстрый, но очень разумный метод. Пожалуйста, посмотрите сроки в моем ответе.
Дауг
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

Вы можете использовать цикл for и or while; однако если вы знаете встроенную функцию Enumerate, то рекомендуется использовать Enumerate. 1

Эймал Дорани
источник
1
Это единственный разумный (читабельный) способ сделать это, когда вам нужно выполнить более сложные операции с элементами списка. Например, если каждый элемент списка представляет собой длинную строку, требующую какого-либо поиска и замены.
not2qubit
8

Чтобы легко заменить все 1с 10в a = [1,2,3,4,5,1,2,3,4,5,1]один можно использовать следующий однострочный комбинацию лямбда + карты, и "Смотри, мама, нет IfS или Форс! :

# 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
источник
Самый медленный метод на сегодняшний день. Вы вызываете lambdaкаждый элемент списка ...
Дауг
4

Ниже приведен очень прямой метод в 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

Этот метод работает. Комментарии приветствуются. Надеюсь, поможет :)

Также попытайтесь понять, как работают решения outis и damzam . Сжатия списков и лямбда-функции являются полезными инструментами.

Ананай Митал
источник
4

В длинных списках и редких случаях использование примерно в 3 раза быстрее list.index()- по сравнению с одношаговыми итерационными методами, представленными в других ответах.

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
источник
Это самый быстрый метод, который я нашел. Пожалуйста, посмотрите сроки в моем ответе. Большой!
Дауг
3

Я знаю, что это очень старый вопрос, и существует множество способов сделать это. Более простой, который я нашел, использует numpyпакет.

import numpy

arr = numpy.asarray([1, 6, 1, 9, 8])
arr[ arr == 8 ] = 0 # change all occurrences of 8 by 0
print(arr)
Тьяго Виейра
источник
3

Мой вариант использования был заменен Noneнекоторым значением по умолчанию.

Я рассчитал подходы к этой проблеме, которые были представлены здесь, в том числе @kxr - using str.count.

Тестовый код в ipython с 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)    

Я получил:

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)

Использование внутреннего str.indexна самом деле быстрее, чем любое ручное сравнение.

Я не знал, будет ли исключение в тесте 4 более трудоемким, чем использование str.count , разница кажется незначительной.

Обратите внимание, что map()(тест 6) возвращает итератор, а не фактический список, таким образом, тест 7.

сойка
источник
2

Вы можете просто использовать понимание списка в python:

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

для вашего случая, когда вы хотите заменить все вхождения 1 на 10, фрагмент кода будет выглядеть так:

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
источник
3
Хотя этот фрагмент кода может решить вопрос, в том числе объяснение действительно помогает улучшить качество вашего сообщения. Помните, что вы отвечаете на вопрос читателей в будущем, и эти люди могут не знать причин, по которым вы предлагаете код. Также постарайтесь не переполнять ваш код пояснительными комментариями, это снижает удобочитаемость кода и пояснений!
Филнор
-1

Найти и заменить только один предмет

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]
Берекет Гебредингл
источник