Видаліть порожні рядки зі списку рядків


683

Я хочу видалити всі порожні рядки зі списку рядків у python.

Моя ідея виглядає приблизно так:

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

Чи є ще якийсь пітонічний спосіб зробити це?


45
@Ivo, жодне з цих тверджень не відповідає дійсності. Ніколи не слід змінювати список, за допомогою якого повторюється ваш повтор, for x in listякщо ви використовуєте, while loopто це добре. продемонстрований цикл видалить порожні рядки, поки не буде більше порожніх рядків, а потім зупиниться. Я насправді навіть не дивився на питання (лише заголовок), але я відповів точно таким же циклом, як і можливість! Якщо ви не хочете використовувати розуміння чи фільтри задля пам’яті, це дуже пітонічне рішення.
ааронастерлінг

4
І все-таки дуже вагомий момент ніколи не змінювати список, який ви повторюєте :)
Едуард Лука

1
@EduardLuca, якщо суть ітерації над списком полягає в тому, щоб змінити його, то це протилежне тому, що ви повинні зробити. Ви просто повинні бути обережними, щоб ви знали, що цим не викликаєте несподіваної поведінки.
JFA

1
@EduardLuca, @JFA: Справа в тому, що він НЕ повторюється над жодним списком. Він би, якби щось написав у формі for var in list:, але ось, він написав while const in list:. яка не повторюється ні над чим. це просто повторення одного і того ж коду, поки умова не буде помилковою.
Каміон

Відповіді:


1154

Я б користувався 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 способи), тому вам знадобиться альтернативний метод.
ХропінняFrog

2
Це зосереджено лише на швидкості, а не на тому, наскільки пітонічним є рішення (питання, яке було задано). Зрозуміння списків - це пітонічне рішення, і фільтр слід застосовувати лише у тому випадку, якщо профілювання доведе, що listcomp є вузьким місцем.
Tritium21

3
@ хто хто згадує-про-чи-маю на увазі-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

він не буде працювати, якщо у вас є простір серед рядка слова. наприклад: ['привіт світ', '', 'привіт', '']. >> ['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, я б використав if! = '', Щоб просто усунути порожні рядки. Подобається це:

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

Це дозволить зберегти в списку жоден тип даних. Крім того, якщо у вашому списку є цілі числа і 0 є одним з них, він також буде збережений.

Наприклад,

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

2
Якщо у ваших списках різні типи (крім жодного), у вас може виникнути більша проблема.
Тритій21

Які типи? Я спробував з int та іншими числовими типами, рядками, списками, записами, множинами та None та жодних проблем там. Я можу побачити, що якщо існують визначені користувачем типи, які не підтримують метод str, можуть виникнути проблеми. Чи варто переживати за будь-якого іншого?
thuuvenkadam

1
Якщо у вас є str_list = [None, '', 0, "Hi", '', "Hello"], це ознака погано розробленого додатка. У тому ж списку не повинно бути більше одного інтерфейсу (типу) та None.
Tritium21

3
Отримання даних з db? список аргументів функції під час автоматичного тестування?
thiruvenkadam

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

Майте на увазі, що якщо ви хочете зберегти пробіли в рядку , ви можете їх ненавмисно видалити, використовуючи деякі підходи. Якщо у вас є цей список

['привіт світ', '', '', 'привіт'], що ти можеш хотіти ['привіт світ', 'привіт']

спочатку обріжте список, щоб перетворити будь-який тип пробілу в порожній рядок:

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
Ласкаво просимо до SO. Вас не проігнорували. На вас не напав анонімний пустохід. Вам надійшли відгуки. Ампліфікуючий: Ваш запропонований перший аргумент для фільтра є гіршим, ніж 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))

PRO:

  • найпростіший;
  • найшвидший (див. орієнтири нижче).

2. Усунути порожнечі після зачистки ...

2.a ... коли рядки НЕ містять пробілів між словами:

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

PRO:

  • невеликий код
  • швидкий (АЛЕ не найшвидший з великими наборами даних завдяки пам’яті, всупереч результатам @ paolo-melchiorre)

2.b ... коли рядки містять пробіли між словами?

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

PRO:

  • найшвидший;
  • зрозумілість коду.

Орієнтири на машині 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 погані.
ankostis

-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.