Удалить пустые строки из списка строк


684

Я хочу удалить все пустые строки из списка строк в Python.

Моя идея выглядит так:

while '' in str_list:
    str_list.remove('')

Есть ли еще питонский способ сделать это?


45
@ Иво, ни одно из этих утверждений не соответствует действительности. Вы никогда не должны изменять список, который вы используете для итерации. for x in listЕсли вы используете, while loopто это нормально. Показанный цикл удалит пустые строки, пока не останется больше пустых строк, а затем остановится. На самом деле я даже не посмотрел на вопрос (только название), но я ответил точно так же, как это возможно! Если вы не хотите использовать понимание или фильтры ради памяти, это очень питонное решение.
Ааронастерлинг

4
Тем не менее, очень важный момент - никогда не менять список, который вы повторяете :)
Эдуард Лука,

1
@EduardLuca, если цель итерации по списку состоит в том, чтобы изменить его, то это противоположно тому, что вы должны сделать. Вам просто нужно быть осторожным, чтобы вы знали, что при этом вы не вызываете неожиданного поведения.
JFA

1
@EduardLuca, @JFA: Дело в том, что он НЕ повторяется ни по одному списку. Он бы, если бы он написал что-то в форме for var in list:, но здесь он написал while const in list:. который не перебирает ничего. это просто повторяет один и тот же код, пока условие не станет ложным.
Камион

Ответы:


1155

Я бы использовал filter:

str_list = filter(None, str_list)
str_list = filter(bool, str_list)
str_list = filter(len, str_list)
str_list = filter(lambda item: item, str_list)

Python 3 возвращает итератор из filter, поэтому должен быть заключен в вызовlist()

str_list = list(filter(None, str_list))

11
Если вы что нажата для выполнения, itertool«sifilter даже faster- >>> timeit('filter(None, str_list)', 'str_list=["a"]*1000', number=100000) 2.3468542098999023; >>> timeit('itertools.ifilter(None, str_list)', 'str_list=["a"]*1000', number=100000) 0.04442191123962402,
Хамфри Богарт

4
@cpburnz Очень верно. Однако ifilterрезультаты оцениваются лениво, а не за один раз - я бы сказал, что в большинстве случаев ifilterэто лучше. Интересно, что использование filterпо-прежнему быстрее, чем завернуть ifilterв listхотя.
Хамфри Богарт

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

2
Это фокусируется только на скорости, а не на том, насколько питонным является решение (вопрос, который был задан). Понимание списка - это питонное решение, и фильтр следует использовать только в том случае, если профилирование доказало, что listcomp является узким местом.
Тритий

3
@ whoever-упоминания о-или-подразумеваемом-Python-3, пожалуйста, просто отредактируйте и обновите ответ. Мы обсуждали только Python 2, когда задавался этот вопрос, даже Python 3 был выпущен почти 2 года назад. Но обновите результаты Python 2 и 3.
livibetter

237

Использование понимания списка - самый питонский способ:

>>> strings = ["first", "", "second"]
>>> [x for x in strings if x]
['first', 'second']

Если список необходимо изменить на месте, поскольку существуют другие ссылки, которые должны видеть обновленные данные, используйте назначение среза:

strings[:] = [x for x in strings if x]

16
Мне нравится это решение, потому что оно легко адаптируется. Если мне нужно , чтобы удалить не только пустые строки , но строки, которые только пробелы, например: [x for x in strings if x.strip()].
Бонд

67

Фильтр на самом деле имеет специальную опцию для этого:

filter(None, sequence)

Это отфильтрует все элементы, которые оцениваются как False. Нет необходимости использовать фактический вызываемый объект, такой как bool, len и так далее.

Это так же быстро, как карта (bool, ...)


5
На самом деле это идиома питона. Это также единственный раз, когда я до сих пор использую filter (), списочные постижения стали повсеместными.
Калейссин

24
>>> lstr = ['hello', '', ' ', 'world', ' ']
>>> lstr
['hello', '', ' ', 'world', ' ']

>>> ' '.join(lstr).split()
['hello', 'world']

>>> filter(None, lstr)
['hello', ' ', 'world', ' ']

Сравнить время

>>> from timeit import timeit
>>> timeit('" ".join(lstr).split()', "lstr=['hello', '', ' ', 'world', ' ']", number=10000000)
4.226747989654541
>>> timeit('filter(None, lstr)', "lstr=['hello', '', ' ', 'world', ' ']", number=10000000)
3.0278358459472656

Обратите внимание, что filter(None, lstr)пустые строки не удаляются с пробелом ' ', они удаляются только ''при удалении ' '.join(lstr).split()обоих.

Чтобы использовать filter()строки без пробелов, это займет намного больше времени:

>>> timeit('filter(None, [l.replace(" ", "") for l in lstr])', "lstr=['hello', '', ' ', 'world', ' ']", number=10000000)
18.101892948150635

это не сработает, если у вас есть место среди строки слова. например: ['hello world', '', 'hello', '']. >> ['helloworld', '', 'hello', ''] Есть ли у вас какое-либо другое решение, позволяющее сохранить пробелы внутри элемента в списке, но удалить другие?
Reihan_amn

Обратите внимание, что filter(None, lstr)пустые строки не удаляются с пробелом' ' Да, потому что это не пустая строка.
AMC

15

Ответ от @ Ib33X потрясающий. Если вы хотите удалить каждую пустую строку, после удаления. вам нужно использовать метод стрип тоже. В противном случае он также вернет пустую строку, если у него есть пробелы. Мол, "" будет действительным и для этого ответа. Таким образом, может быть достигнуто путем.

strings = ["first", "", "second ", " "]
[x.strip() for x in strings if x.strip()]

Ответ на это будет ["first", "second"].
Если вы хотите использовать filterметод вместо этого, вы можете сделать как
list(filter(lambda item: item.strip(), strings)). Это дает тот же результат.


12

Вместо if x я бы использовал if X! = '', Чтобы просто удалить пустые строки. Нравится:

str_list = [x for x in str_list if x != '']

Это сохранит тип данных None в вашем списке. Кроме того, если в вашем списке есть целые числа и 0 среди них одно, оно также будет сохранено.

Например,

str_list = [None, '', 0, "Hi", '', "Hello"]
[x for x in str_list if x != '']
[None, 0, "Hi", "Hello"]

2
Если в ваших списках есть разные типы (кроме «Нет»), у вас могут быть большие проблемы.
Тритий

Какие типы? Я пробовал с int и другими числовыми типами, строками, списками, кортежами, множествами и None и никаких проблем там нет. Я мог видеть, что, если есть какие-либо определенные пользователем типы, которые не поддерживают метод str, может возникнуть проблема. Должен ли я беспокоиться о других?
Триувенкадам

1
Если у вас есть str_list = [None, '', 0, "Hi", '', "Hello"], это признак плохо разработанного приложения. Вы не должны иметь более одного интерфейса (типа) и None в одном списке.
Tritium21

3
Получение данных из БД? список аргументов для функции при выполнении автоматического тестирования?
Триувенкадам

3
Это обычно кортежи.
Tritium21,

7

В зависимости от размера вашего списка, это может быть наиболее эффективным, если вы используете list.remove (), а не создаете новый список:

l = ["1", "", "3", ""]

while True:
  try:
    l.remove("")
  except ValueError:
    break

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


1
Вы можете редактировать список на месте, выполнив ary[:] = [e for e in ary if e]. Гораздо чище и не использует исключения для управления потоком.
Кшиштоф Карский

2
Ну, это не совсем "на месте" - я уверен, что это создает новый список и просто присваивает его старому имени.
Эндрю Джаффе

Это работает очень плохо, так как хвост данных перетасовывается в памяти при каждом удалении. Лучше убрать все в один удар.
Вим

7

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

['hello world', '', '', 'hello'] что вы можете захотеть ['hello world', 'hello']

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

space_to_empty = [x.strip() for x in _text_list]

затем удалите пустую строку из списка

space_clean_list = [x for x in space_to_empty if x]

если вы хотите сохранить пробелы внутри строки, вы можете непреднамеренно удалить их, используя некоторые подходы. Вам нравится такой подход?
AMC

Спасибо, чувак, это сработало для меня с небольшими изменениями. то естьspace_clean_list = [x.strip() for x in y if x.strip()]
Мухаммед Мехран Хан Аттари

6

Используйте filter:

newlist=filter(lambda x: len(x)>0, oldlist) 

Недостатки использования фильтра, как указано выше, заключаются в том, что он медленнее альтернатив; Кроме того, lambdaобычно это дорого.

Или вы можете пойти на самый простой и самый итеративный из всех:

# I am assuming listtext is the original list containing (possibly) empty items
for item in listtext:
    if item:
        newlist.append(str(item))
# You can remove str() based on the content of your original list

это самый интуитивный из методов и делает это в приличное время.


9
Добро пожаловать в ТАК. Вы не были проигнорированы. Вы не подвергались нападению со стороны какого-либо необычного даунотера. Вам дали отзыв. Усиление: предложенный вами первый аргумент для фильтра хуже, чем тот, lambda x: len(x)который хуже, чем lambda x : xкоторый является худшим из 4 решений в выбранном ответе. Правильное функционирование является предпочтительным, но не достаточным. Наведите курсор на кнопку понижения: там написано: «Этот ответ бесполезен».
Джон Мачин

5

Как сообщает Aziz, Alto filter(None, lstr) не удаляет пустые строки с пробелом, ' 'но если вы уверены, что lstr содержит только строку, вы можете использоватьfilter(str.strip, lstr)

>>> lstr = ['hello', '', ' ', 'world', ' ']
>>> lstr
['hello', '', ' ', 'world', ' ']
>>> ' '.join(lstr).split()
['hello', 'world']
>>> filter(str.strip, lstr)
['hello', 'world']

Сравните время на моем компьютере

>>> from timeit import timeit
>>> timeit('" ".join(lstr).split()', "lstr=['hello', '', ' ', 'world', ' ']", number=10000000)
3.356455087661743
>>> timeit('filter(str.strip, lstr)', "lstr=['hello', '', ' ', 'world', ' ']", number=10000000)
5.276503801345825

Самое быстрое решение для удаления ''и очистки строк с пробелом ' 'остается ' '.join(lstr).split().

Как сообщается в комментарии, ситуация отличается, если ваши строки содержат пробелы.

>>> lstr = ['hello', '', ' ', 'world', '    ', 'see you']
>>> lstr
['hello', '', ' ', 'world', '    ', 'see you']
>>> ' '.join(lstr).split()
['hello', 'world', 'see', 'you']
>>> filter(str.strip, lstr)
['hello', 'world', 'see you']

Вы можете видеть, что filter(str.strip, lstr)сохраняются строки с пробелами, но ' '.join(lstr).split()разбиваются эти строки.


1
Это работает только если ваши строки не содержат пробелов. В противном случае, вы также разбиваете эти строки.
Phillyslick

1
@BenPolinsky, как вы сообщили, joinрешение разделит строки пробелом, а фильтр - нет. Спасибо за ваш комментарий Я улучшил свой ответ.
Паоло Мельхиорре

-1

Подведите лучшие ответы:

1. Ликвидация пустых БЕЗ зачистки:

Таким образом, строки всего пространства сохраняются:

slist = list(filter(None, slist))

PROs:

  • Простейшие;
  • самый быстрый (см. тесты ниже).

2. Устранить пустоты после зачистки ...

2.a ... когда строки НЕ содержат пробелов между словами:

slist = ' '.join(slist).split()

PROs:

  • маленький код
  • быстро (НО не самый быстрый с большими наборами данных из-за памяти, в отличие от результатов @ paolo-melchiorre)

2.b ... когда строки содержат пробелы между словами?

slist = list(filter(str.strip, slist))

PROs:

  • быстрый;
  • понятность кода.

Тесты на машине 2018 года:

## Build test-data
#
import random, string
nwords = 10000
maxlen = 30
null_ratio = 0.1
rnd = random.Random(0)                  # deterministic results
words = [' ' * rnd.randint(0, maxlen)
         if rnd.random() > (1 - null_ratio)
         else
         ''.join(random.choices(string.ascii_letters, k=rnd.randint(0, maxlen)))
         for _i in range(nwords)
        ]

## Test functions
#
def nostrip_filter(slist):
    return list(filter(None, slist))

def nostrip_comprehension(slist):
    return [s for s in slist if s]

def strip_filter(slist):
    return list(filter(str.strip, slist))

def strip_filter_map(slist): 
    return list(filter(None, map(str.strip, slist))) 

def strip_filter_comprehension(slist):  # waste memory
    return list(filter(None, [s.strip() for s in slist]))

def strip_filter_generator(slist):
    return list(filter(None, (s.strip() for s in slist)))

def strip_join_split(slist):  # words without(!) spaces
    return ' '.join(slist).split()

## Benchmarks
#
%timeit nostrip_filter(words)
142 µs ± 16.8 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

%timeit nostrip_comprehension(words)
263 µs ± 19.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit strip_filter(words)
653 µs ± 37.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit strip_filter_map(words)
642 µs ± 36 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit strip_filter_comprehension(words)
693 µs ± 42.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit strip_filter_generator(words)
750 µs ± 28.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit strip_join_split(words)
796 µs ± 103 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

s and s.strip()можно упростить до всего s.strip().
AMC

s and s.strip()нужен, если мы хотим полностью воспроизвести filter(None, words)принятый ответ. Я исправил x2 пример функции выше и удалил x2 плохих.
Анкостис

-2

Для списка с комбинацией пробелов и пустых значений используйте простое понимание списка -

>>> s = ['I', 'am', 'a', '', 'great', ' ', '', '  ', 'person', '!!', 'Do', 'you', 'think', 'its', 'a', '', 'a', '', 'joke', '', ' ', '', '?', '', '', '', '?']

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

>>> d = [x for x in s if x.strip()]
>>> d
>>> d = ['I', 'am', 'a', 'great', 'person', '!!', 'Do', 'you', 'think', 'its', 'a', 'a', 'joke', '?', '?']
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.