Удаление элемента из списка, соответствующего подстроке

Как удалить элемент из списка, если он соответствует подстроке?

Я попытался удалить элемент из списка, используя метод pop() и enumerate, но, похоже, мне не хватает нескольких смежных элементов, которые необходимо удалить:

sents = ['@$\tthis sentences needs to be removed', 'this doesnt',
     '@$\tthis sentences also needs to be removed',
     '@$\tthis sentences must be removed', 'this shouldnt',
     '# this needs to be removed', 'this isnt',
     '# this must', 'this musnt']

for i, j in enumerate(sents):
  if j[0:3] == "@$\t":
    sents.pop(i)
    continue
  if j[0] == "#":
    sents.pop(i)

for i in sents:
  print i

Выход:

this doesnt
@$  this sentences must be removed
this shouldnt
this isnt
#this should
this musnt

Желаемый результат:

this doesnt
this shouldnt
this isnt
this musnt

person alvas    schedule 01.10.2012    source источник
comment
Классический случай удаления элементов из списка во время повторения этого списка. Прочитайте десятки других вопросов о переполнении стека, которые относятся к этому. Также см. примечание в документации.   -  person John Y    schedule 01.10.2012
comment
вы всегда должны избегать изменения длины контейнера при его повторении, это рецепт катастрофы   -  person wim    schedule 01.10.2012
comment
Как правило, лучше создать новый отфильтрованный список, чем пытаться изменить список на месте. Неизменяемые алгоритмы всегда легче обдумать (хотя не всегда проще понять, как писать). Когда вы просто заменяете значения, иногда повышение эффективности работы на месте превосходит это, но когда вы удаляете или вставляете в середину списка, вы обычно получаете хуже эффективность с вашей менее надежной логикой.   -  person abarnert    schedule 01.10.2012


Ответы (3)


Как насчет чего-то простого, например:

>>> [x for x in sents if not x.startswith('@$\t') and not x.startswith('#')]
['this doesnt', 'this shouldnt', 'this isnt', 'this musnt']
person D.Shawley    schedule 01.10.2012

Это должно работать:

[i for i in sents if not ('@$\t' in i or '#' in i)]

Если вам нужны только вещи, которые начинаются с указанных сентенциальных, используйте метод str.startswith(stringOfInterest)

person mjgpy3    schedule 01.10.2012
comment
Я бы сказал, что этот лучше двух других, потому что не предполагает, что подстроки находятся в начале - person Frikster; 21.07.2015

Еще одна техника с использованием filter

filter( lambda s: not (s[0:3]=="@$\t" or s[0]=="#"), sents)

Проблема с вашим оригинальным подходом заключается в том, что когда вы находитесь в элементе списка i и определяете, что его следует удалить, вы удаляете его из списка, что перемещает элемент i+1 в позицию i. На следующей итерации цикла вы находитесь в индексе i+1, но на самом деле элемент i+2.

Есть смысл?

person cod3monk3y    schedule 01.10.2012
comment
Спасибо за объяснение! выталкивать список во время перечисления действительно глупо. хахаха.. - person alvas; 01.10.2012
comment
Только это сработало для меня: sends = list(filter( lambda s: not (s[0:3]==@$\t or s[0]==#), sends)) - person chainstair; 30.04.2020