У меня есть список строк и список префиксов. Я хочу удалить все элементы из списка строк, которые начинаются с префикса из списка префиксов.
Я использовал цикл for
, но почему он не работает?
list_of_strings = ['test-1: foo', 'test-2: bar', 'test-3: cat']
list_of_prefixes = ['test1', 'test-2']
final_list = []
for i in list_of_strings:
for j in list_of_prefixes:
if not i.startswith(j):
final_list.append(i)
print(list(set(final_list)))
В настоящее время вывод
['test-3: cat', 'test-1: foo', 'test-2: bar']
Результат, который я хочу получить, это
final_list = ['test-3: cat']
Вы можете использовать startswith()
с flag
:
def _filter(list_of_strings, list_of_prefixes):
final_list = []
for i in list_of_strings:
flag = False
for j in list_of_prefixes:
if i.startswith(j):
flag = True
break
if not flag:
final_list.append(i)
return final_list
list_of_strings = ['test-1: foo', 'test-2: bar', 'test-3: cat']
list_of_prefixes = ['test-1', 'test-2']
print(_filter(list_of_strings, list_of_prefixes))
['test-3: cat']
Проблема с вашей реализацией заключается в том, что для каждого префикса вы просматриваете весь list_of_strings
и добавляете к конечному результату final_list
все элементы, которые начинаются не с этого префикса, включая строки, которые могут начинаться с других префиксов в list_of_prefixes
(которые , в конечном счете, вы не захотите оставлять).
Например:
list_of_strings
элементом является 'test-1: foo'
, поэтому вы перебираете list_of_prefixes
, первый префикс — 'test-1'
. Круто, ты не добавляешь 'test-1: foo'
к final_list
.'test-2'
. Вы проверяете его на 'test-1: foo'
, и он проходит проверку if not i.startswith(j)
, так что в конечном итоге вы все равно добавляете 'test-1: foo'
к final_list
!В качестве быстрого исправления вашей собственной реализации, без особых изменений в вашей логике, я предлагаю: вместо того, чтобы начинать с пустого набора и добавлять к нему, начните с набора всех list_of_strings
и удаляйте ненужное в своих итерациях!
Так:
final_list = set(list_of_strings)
for i in list_of_strings:
for j in list_of_prefixes:
if i.startswith(j):
final_list.remove(i)
Со временем, по мере вашего знакомства с Python, вы поймете, что большинство этих базовых операций (например, фильтрация) уже реализованы встроенными модулями. Есть много способов сделать это без пользовательских циклов. Например, используя filter
и списки:
[s for s in list_of_strings if not any(filter(s.startswith, list_of_prefixes))]
Какая забавная головоломка. Я предполагаю, что первая запись в префиксах — «test-1».
Причина, по которой ваш цикл не работает, заключается в том, что вы фактически начинаете заново каждый раз, когда выполняете запрос, помещаете результат в окончательный массив и в конечном итоге получаете по одному ответу каждого типа.
Поскольку вы эффективно фильтруете только то, что хотите видеть, вы можете достичь своей цели следующим образом:
list_of_strings = ['test-1: foo', 'test-2: bar', 'test-3: cat']
list_of_prefixes = ['test-1', 'test-2']
final_list = list_of_strings
for prefix in list_of_prefixes:
result = list(filter(lambda x: x.startswith(prefix), final_list))
for ele in result:
final_list.remove(ele)
print(final_list)
Я сохранил исходные массивы на тот случай, если они понадобятся вам в дальнейшем в вашем коде.
Ваш подход не работает, потому что вы потенциально выполняете добавление для каждого элемента в list_of_prefixes
, но если строка начинается с одного из префиксов, она гарантированно не начинается с одного из других, поэтому все они добавляются.
С помощью списков, выражений-генераторов и any
это очень просто.
>>> list_of_strings = ['test-1: foo', 'test-2: bar', 'test-3: cat']
>>> list_of_prefixes = ['test1', 'test-2']
>>> filtered = [
... s
... for s in list_of_strings
... if not any(s.startswith(p) for p in list_of_prefixes)
... ]
>>> filtered
['test-1: foo', 'test-3: cat']
Обратите внимание, что 'test-1: foo'
не начинается с 'test1'
или 'test-2'
. Если бы вы хотели, чтобы list_of_prefixes
включал 'test-1'
, вы получили бы ожидаемый результат.
Вы можете использовать полный список в одну строку, например:
list_of_strings = ['test-1: foo', 'test-2: bar', 'test-3: cat']
list_of_prefixes = ['test1', 'test-2']
new = [x for x in list_of_strings if x.split(':')[0] not in list_of_prefixes]
print(new)
Выход:
['test-1: foo', 'test-3: cat']
Вы можете упростить свой код. startswith
изначально принимает несколько префиксов и возвращает True
, если один из них присутствует. Однако этот аргумент должен быть кортежем, а не списком, поэтому я переименовал его.
list_of_strings = ['test-1: foo', 'test-2: bar', 'test-3: cat']
# switch to a tuple
tuple_of_prefixes = ('test-1', 'test-2')
final_list = []
for i in list_of_strings:
if not i.startswith(tuple_of_prefixes):
final_list.append(i)
print(list(set(final_list)))
(Кроме того, согласно вашему изложению, я думаю, вы хотели исключить test-1
, а не test1
.)
Демо: https://ideone.com/0NvI5z
Очевидно, если хотите, это можно превратить в понимание списка.
этот код изменяет исходные списки,
final_list = list_of_strings
не создает копию