2009-01-30 2 views
116

Можно ли одновременно удалить несколько элементов из списка? Если я хочу удалить элементы с индексами 0 и 2 и попробовать что-то вроде del somelist [0], а затем del somelist [2], второй оператор фактически удалит somelist [3].Удаление нескольких элементов из списка

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

ответ

72

Вероятно, не самое лучшее решение для этой проблемы:

indices = 0, 2 
somelist = [i for j, i in enumerate(somelist) if j not in indices] 
+0

Почти, только если вы удалите весь список. это будет len (индексы) * len (somelist). Он также создает копию, которая может быть или не нужна. –

+1

поиск не является линейным – SilentGhost

+0

, если вы проверяете значение в списке, это так. оператор «in» работает с значениями списка, тогда как он работает с ключами dict. Если я ошибаюсь, укажите мне pep/reference –

90

Если вы удаляете несколько несмежных элементов, то то, что вы описываете, является лучшим способом (и да, обязательно начинайте с самого высокого индекса).

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

a[2:10] = [] 
+68

Вы также можете сказать 'дель а [2:10]' с тем же эффектом. – sth

+5

@sth Интересно, что del немного быстрее, чем назначение. – thefourtheye

17

В функции:

def multi_delete(list_, *args): 
    indexes = sorted(list(args), reverse=True) 
    for index in indexes: 
     del list_[index] 
    return list_ 

Выполняется в n log (n) Время, которое должно сделать это fa еще не верьте.

+1

Версия с args.sort(). reverse() определенно лучше. Также бывает работать с диктофонами вместо того, чтобы бросать или, что еще хуже, бессильно развращать. – 2009-01-30 22:45:23

+0

sort() не определен для кортежа, вам придется сначала преобразовать его в список. sort() возвращает None, поэтому вы не можете использовать reverse(). – SilentGhost

+0

@ R. Pate: Я удалил первую версию по этой причине. Благодарю. @ SilentGhost: Исправлено. –

0

Вы можете сделать это на дикторе, а не в списке. В элементе списка последовательно. В dict они зависят только от индекса.

Простой код просто объяснить это делая:

>>> lst = ['a','b','c'] 
>>> dct = {0: 'a', 1: 'b', 2:'c'} 
>>> lst[0] 
'a' 
>>> dct[0] 
'a' 
>>> del lst[0] 
>>> del dct[0] 
>>> lst[0] 
'b' 
>>> dct[0] 
Traceback (most recent call last): 
    File "<pyshell#19>", line 1, in <module> 
    dct[0] 
KeyError: 0 
>>> dct[1] 
'b' 
>>> lst[1] 
'c' 

Путь к "преобразовать" список в Словаре является:

>>> dct = {} 
>>> for i in xrange(0,len(lst)): dct[i] = lst[i] 

и обратное:

lst = [dct[i] for i in sorted(dct.keys())] 

В любом случае, я думаю, что лучше начать удаление с более высокого индекса, как вы помощь.

+0

Предоставляет ли Python гарантию [dct [i] для i в dct] всегда будет использовать возрастающие значения i? Если это так, список (dct.values ​​()), безусловно, лучше. – 2009-01-30 22:41:39

+0

Я не думал об этом. Ты прав. Нет гарантии, поскольку я читаю [здесь] [1], что элементы будут выбраны по порядку или, по крайней мере, ожидаемому порядку. Я редактировал. [1]: http://docs.python.org/library/stdtypes.html#dict.items –

+2

Этот ответ говорит о словарях принципиально неправильным образом. Словарь имеет KEYS (не INDICES). Да, пары ключ/значение не зависят друг от друга. Нет, неважно, в каком порядке вы удаляете записи. Преобразование в словарь только для удаления некоторых элементов из списка будет излишним. – ToolmakerSteve

0

Я на самом деле думаю, что из двух способов сделать это:

  1. среза список, как (это удалит 1-й, 3-й и 8-элементы)

    somelist = somelist [1: 2] + somelist [3: 7] + somelist [8:]

  2. сделать это на месте, но по одному за раз:

    somelist.pop (2) somelist.pop (0)

10

Итак, вы по сути хотите удалить несколько элементов за один проход? В этом случае позиция следующего элемента для удаления будет смещена, однако многие из них были удалены ранее.

Наша цель - удалить все гласные, которые предварительно вычисляются как индексы 1, 4 и 7. Обратите внимание, что его важные индексы to_delete находятся в порядке возрастания, иначе это не сработает.

to_delete = [1, 4, 7] 
target = list("hello world") 
for offset, index in enumerate(to_delete): 
    index -= offset 
    del target[index] 

Было бы сложнее, если бы вы хотели удалить элементы в любом порядке. IMO, сортировка to_delete может быть проще, чем выяснять, когда вы должны или не должны вычесть из index.

16

Как специализация ответа Грега, вы можете использовать расширенный синтаксис среза. например. Если вы хотите, чтобы удалить элементы 0 и 2:

>>> a= [0, 1, 2, 3, 4] 
>>> del a[0:3:2] 
>>> a 
[1, 3, 4] 

Это не распространяется на любой произвольный выбор, конечно, но это, безусловно, может работать для удаления любых двух элементов.

3

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

>>> a = range(10) 
>>> remove = [0,4,5] 
>>> from collections import deque 
>>> deque((list.pop(a, i) for i in sorted(remove, reverse=True)), maxlen=0) 

>>> timeit.timeit('[i for j, i in enumerate(a) if j not in remove]', setup='import random;remove=[random.randrange(100000) for i in range(100)]; a = range(100000)', number=1) 
0.1704120635986328 

>>> timeit.timeit('deque((list.pop(a, i) for i in sorted(remove, reverse=True)), maxlen=0)', setup='from collections import deque;import random;remove=[random.randrange(100000) for i in range(100)]; a = range(100000)', number=1) 
0.004853963851928711 
+0

+1: Интересное использование deque для выполнения действия как части выражения, а не для блока «для ..:».Однако, для этого простого случая, я считаю, что для Никхила блок более читабельным. – ToolmakerSteve

5

я полный новичок в Python, и мое программирование на данный момент является грубым и грязным, мягко говоря, но мое решение было использовать комбинацию из основных команд, которые я узнал в начале учебников:

SomeList = [1,2,3,4,5,6,7,8,10] 
Rem = [0,5,7] 

for i in Rem: 
    SomeList[i]='!' # mark for deletion 

for i in range(0,SomeList.count('!')): 
    SomeList.remove('!') # remove 
print SomeList 

Очевидно, что из-за необходимости выбора символа «знак-для-удаления» это имеет свои ограничения.

Что касается производительности, как размера шкалы списка, я уверен, что мое решение является неоптимальным. Тем не менее, это просто, что я надеюсь обратиться к другим новичкам и будет работать в простых случаях, когда SomeList имеет известный формат, например, всегда числовой ...

+4

в python редко можно перечислить имена переменных. –

+1

вместо использования '!' как ваш особый символ, используйте None. Это удерживает каждый символ действительным и освобождает ваши возможности. – portforwardpodcast

4

Вот альтернатива, которая не использует перечисление() для создания кортежей (как в оригинальном ответе SilentGhost).

Это кажется более читаемым для меня. (Может быть, я буду чувствовать себя иначе, если бы у меня была привычка использовать перечисление.) CAVEAT: Я не тестировал производительность двух подходов.

# Returns a new list. "lst" is not modified. 
def delete_by_indices(lst, indices): 
    indices_as_set = set(indices) 
    return [ lst[i] for i in xrange(len(lst)) if i not in indices_as_set ] 

ПРИМЕЧАНИЕ: Синтаксис Python 2.7. Для Python 3, xrange =>range.

Использование:

lst = [ 11*x for x in xrange(10) ] 
somelist = delete_by_indices(lst, [0, 4, 5]) 

somelist:

[11, 22, 33, 66, 77, 88, 99] 

--- БОНУС ---

Удаление нескольких значений из списка. То есть, у нас есть ценности, которые мы хотим удалить:

# Returns a new list. "lst" is not modified. 
def delete__by_values(lst, values): 
    values_as_set = set(values) 
    return [ x for x in lst if x not in values_as_set ] 

Использование:

somelist = delete__by_values(lst, [0, 44, 55]) 

somelist:

[11, 22, 33, 66, 77, 88, 99] 

Это тот же ответ, как и раньше, но на этот раз мы поставили ЦЕННОСТИ, подлежащие удалению [0, 44, 55].

+0

Я решил, что @ SilentGhost's было трудно читать только из-за не описательных имен переменных, используемых для результата перечисления. Кроме того, parens облегчили бы чтение. Итак, вот как я бы сказал свое решение (с добавлением «set» для производительности): '[значение для (i, value) в перечислении (lst), если я не в наборе (индексы)]'. Но я оставлю свой ответ здесь, потому что я также показываю, как удалить значения. Это более простой случай, но может помочь кому-то. – ToolmakerSteve

+0

@ Veedrac- спасибо; Я переписал, чтобы сначала создать набор. Как вы думаете, быстрее ли решение, чем SilentGhost? (Я не считаю это достаточно важным, чтобы на самом деле это время, просто спрашивая ваше мнение.) Аналогичным образом, я бы перезаписал версию SilentGhost как 'indices_as_set = set (индексы)', '[значение для (i, value) в перечислении (lst), если я не в index_as_set] ', чтобы ускорить его. – ToolmakerSteve

+0

[Я согласен с этим переписать:).] (Http://stackoverflow.com/a/26084037/1763356) – Veedrac

1

Метод удаления вызывает много сдвига элементов списка. Я думаю, что лучше сделать копию:

... 
new_list = [] 
for el in obj.my_list: 
    if condition_is_true(el): 
     new_list.append(el) 
del obj.my_list 
obj.my_list = new_list 
... 
2

Это было упомянуто, но почему-то никто не смог на самом деле правильно это понять.

На O(n) растворе будет:

indices = {0, 2} 
somelist = [i for j, i in enumerate(somelist) if j not in indices] 

Это очень близко к SilentGhost's version, но добавляет две фигурные скобки.

+0

Это не 'O (n)', если вы подсчитываете запросы, которые принимают 'log (len (индексы))' для каждой итерации. –

+0

@MadPhysicist 'j not in indices' является' O (1) '. – Veedrac

+0

Я не уверен, как вы получите этот номер. Поскольку индексы являются множеством, 'j не в индексах 'по-прежнему требует поиска, который является« O (log (len (индексы))). Хотя я согласен, что поиск в 2-элементном наборе квалифицируется как «O (1)», в общем случае он будет «O (log (N))». В любом случае 'O (N log (N))' все еще бьет 'O (N^2) '. –

86

По какой-то причине мне не понравился ни один из ответов здесь. Да, они работают, но, строго говоря, большинство из них не удаляют элементы в списке, не так ли? (Но сделав копию, а затем заменив исходную на отредактированную копию).

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

Есть ли причина для этого? я бы просто сделать:

for i in sorted(indices, reverse=True): 
    del somelist[i] 

Если вы действительно не хотите удалять элементы в обратном направлении, то я думаю, вы должны просто deincrement значения индексов, которые больше, чем последний удаляемого индекса (не может реально использовать тот же индекс, поскольку у вас есть другой список) или используйте копию списка (который не будет «удалять», а заменяет оригинал отредактированной копией).

Я пропустил что-то здесь, по какой-либо причине НЕ удалять в обратном порядке?

+1

Я знаю, что на это был дан ответ почти год назад, но я хотел echo @portforwardpodcast, что ваш ответ превосходный, по-прежнему полезен и слишком хорош. Спасибо и спасибо за полезный комментарий. – andrewfarah

+1

Я не знаю, почему это не было выбрано в качестве принятого ответа !. Спасибо за это. – swathis

1

технически, ответ НЕТ, что невозможно удалить два объекта в одно и то же время. Тем не менее, можно удалить два объекта в одной строке красивого питона.

del (foo['bar'],foo['baz']) 

будет recusrively удалить foo['bar'], то foo['baz']

+0

Это удаляет из объекта dict, а не список, но я все еще нахожу +1, потому что это чертовски красиво! –

13

Вы можете использовать numpy.delete следующим образом:

import numpy as np 
a = ['a', 'l', 3.14, 42, 'u'] 
I = [0, 2] 
np.delete(a, I).tolist() 
# Returns: ['l', '42', 'u'] 

Если вы не возражаете, в конечном итоге с numpy массива в конце концов, вы можете оставьте вне .tolist(). Вы также должны увидеть некоторые важные улучшения скорости, сделав это более масштабируемым решением. Я не тестировал это, но numpy операции скомпилированы кода, написанного либо на C, либо на Fortran.

+0

Общее решение, когда элементы не последовательны +1 –

+0

вопрос здесь, как насчет удаления ['a', 42]. – evanhutomo

1

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

stuff = ['a', 'b', 'c', 'd', 'e', 'f', 'woof'] 
index = [0, 3, 6] 
new = [i for i in stuff if stuff.index(i) not in index] 

Это возвращает:

['b', 'c', 'e', 'f'] 
1

мы можем сделать это путем использования для цикла Перебор индексов после сортировки индексов список в порядке убывания

mylist=[66.25, 333, 1, 4, 6, 7, 8, 56, 8769, 65] 
indexes = 4,6 
indexes = sorted(indexes, reverse=True) 
for i in index: 
    mylist.pop(i) 
print mylist 
1

Для индексов 0 и 2 из списка A:

for x in (2,0): listA.pop(x) 

Для некоторых случайных индексов для удаления из lišta:

indices=(5,3,2,7,0) 
for x in sorted(indices)[::-1]: listA.pop(x) 
0

Обобщить комментарий от @sth. Удаление элемента в любом классе, которое реализует abc.MutableSequence, а в list, в частности, осуществляется с помощью метода __delitem__ magic. Этот метод работает аналогично __getitem__, то есть он может принимать либо целое число, либо фрагмент. Вот пример:

class MyList(list): 
    def __delitem__(self, item): 
     if isinstance(item, slice): 
      for i in range(*item.indices(len(self))): 
       self[i] = 'null' 
     else: 
      self[item] = 'null' 


l = MyList(range(10)) 
print(l) 
del l[5:8] 
print(l) 

Это выведет

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
[0, 1, 2, 3, 4, 'null', 'null', 'null', 8, 9] 
0

Импорт его только по этой причине может быть излишним, но если вы будете использовать pandas в любом случае, то решение является простым и понятным:

import pandas as pd 
stuff = pd.Series(['a','b','a','c','a','d']) 
less_stuff = stuff[stuff != 'a'] # define any condition here 
# results ['b','c','d'] 
1

Я хотел бы сравнить различные решения, которые упростили поворот ручек.

Сначала я создал мои данные:

import random 

N = 16 * 1024 
x = range(N) 
random.shuffle(x) 
y = random.sample(range(N), N/10) 

Тогда я определил свои функции:

def list_set(value_list, index_list): 
    index_list = set(index_list) 
    result = [value for index, value in enumerate(value_list) if index not in index_list] 
    return result 

def list_del(value_list, index_list): 
    for index in sorted(index_list, reverse=True): 
     del(value_list[index]) 

def list_pop(value_list, index_list): 
    for index in sorted(index_list, reverse=True): 
     value_list.pop(index) 

Тогда я использовал timeit сравнить решения:

import timeit 
from collections import OrderedDict 

M = 1000 
setup = 'from __main__ import x, y, list_set, list_del, list_pop' 
statement_dict = OrderedDict([ 
    ('overhead', 'a = x[:]'), 
    ('set', 'a = x[:]; list_set(a, y)'), 
    ('del', 'a = x[:]; list_del(a, y)'), 
    ('pop', 'a = x[:]; list_pop(a, y)'), 
]) 

overhead = None 
result_dict = OrderedDict() 
for name, statement in statement_dict.iteritems(): 
    result = timeit.timeit(statement, number=M, setup=setup) 
    if overhead is None: 
     overhead = result 
    else: 
     result = result - overhead 
     result_dict[name] = result 

for name, result in result_dict.iteritems(): 
    print "%s = %7.3f" % (name, result) 

Выход

set = 1.711 
del = 3.450 
pop = 3.618 

Таким образом, генератор с индексами в set был победителем. И del немного быстрее, чем pop.

+0

Спасибо за это сравнение, это привело меня к тому, что я сделал свои собственные тесты (на самом деле просто заимствовал ваш код) и для небольшого количества элементов для удаления, накладные расходы на создание SET делает его наихудшим решением (используйте 10, 100, 500 для длина «y», и вы увидите). Как правило, это зависит от приложения. – tglaria

0
l = ['a','b','a','c','a','d'] 
to_remove = [1, 3] 
[l[i] for i in range(0, len(l)) if i not in to_remove]) 

Это в основном то же самое, что и главный проголосовавший ответ, а другой способ его записи. Обратите внимание, что использование l.index() не является хорошей идеей, потому что не может обрабатывать дублированные элементы в списке.

0

Вы можете использовать эту логику:

my_list = ['word','yes','no','nice'] 

c=[b for i,b in enumerate(my_list) if not i in (0,2,3)] 

print c 
Смежные вопросы