Hapus string kosong dari daftar string

682

Saya ingin menghapus semua string kosong dari daftar string dengan python.

Ide saya terlihat seperti ini:

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

Apakah ada cara pythonic lain untuk melakukan ini?

zerodx
sumber
45
@ Ivo, kedua pernyataan itu tidak benar. Anda seharusnya tidak pernah memodifikasi daftar yang berulang Anda gunakan for x in listJika Anda menggunakan while loopmaka tidak apa-apa. loop yang ditunjukkan akan menghapus string kosong hingga tidak ada lagi string kosong dan kemudian berhenti. Sebenarnya saya bahkan belum melihat pertanyaan (hanya judulnya) tetapi saya menjawab dengan kemungkinan yang sama persis! Jika Anda tidak ingin menggunakan pemahaman atau filter demi memori, ini adalah solusi yang sangat pythonic.
aaronasterling
4
Masih poin yang sangat valid untuk tidak pernah mengubah daftar yang sudah Anda ulangi :)
Eduard Luca
1
@ EduardLuca jika titik iterasi daftar adalah untuk mengubahnya, maka itu kebalikan dari apa yang harus Anda lakukan. Anda hanya harus berhati-hati agar Anda tahu bahwa Anda tidak menyebabkan perilaku yang tidak terduga dengan melakukannya.
JFA
1
@EduardLuca, @JFA: Intinya adalah dia TIDAK mengulangi daftar apa pun. Dia akan melakukannya jika dia telah menulis sesuatu dalam bentuk for var in list:, tetapi di sini, dia telah menulis while const in list:. yang tidak mengulangi apa pun. itu hanya mengulang kode yang sama sampai suatu kondisi salah.
Camion

Jawaban:

1150

Saya akan menggunakan 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 mengembalikan iterator dari filter, jadi harus dibungkus dengan panggilan kelist()

str_list = list(filter(None, str_list))
livibetter
sumber
11
Jika Anda yang ditekan untuk kinerja, itertool'sifilter bahkan cepat- >>> 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.
Humphrey Bogart
4
@ cpburnz Sangat benar. Namun, dengan ifilterhasil dievaluasi dengan malas, tidak sekaligus - saya berpendapat bahwa untuk sebagian besar kasus ifilterlebih baik. Semenarik yang menggunakan filtermasih lebih cepat daripada membungkus sebuah ifilterdalam listsekalipun.
Humphrey Bogart
3
Jika Anda melakukan ini pada daftar angka, perhatikan bahwa nol juga akan dihapus (catatan: Saya hanya menggunakan 3 metode pertama), jadi Anda akan memerlukan metode alternatif.
SnoringFrog
2
Ini hanya berfokus pada kecepatan, bukan pada seberapa pythonic solusinya (pertanyaan yang diajukan). List Comprehensions adalah solusi pythonic, dan filter hanya boleh digunakan jika profiling telah membuktikan bahwa listcomp adalah hambatan.
Tritium21
3
@ siapa pun yang menyebutkan tentang atau menyiratkan-Python-3, harap hanya mengedit dan memperbarui jawabannya. Kami hanya mendiskusikan untuk Python 2 ketika pertanyaan ini diajukan, bahkan Python 3 dirilis hampir 2 tahun. Tetapi perbarui hasil Python 2 dan 3.
livibetter
236

Menggunakan pemahaman daftar adalah cara yang paling Pythonic:

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

Jika daftar harus diubah di tempat, karena ada referensi lain yang harus melihat data yang diperbarui, kemudian gunakan tugas irisan:

strings[:] = [x for x in strings if x]
Ib33X
sumber
16
Saya suka solusi ini karena mudah beradaptasi. Jika saya perlu untuk menghapus tidak hanya string kosong tapi string yang hanya spasi, misalnya: [x for x in strings if x.strip()].
Obligasi
67

filter sebenarnya memiliki opsi khusus untuk ini:

filter(None, sequence)

Ini akan memfilter semua elemen yang mengevaluasi ke False. Tidak perlu menggunakan callable yang sebenarnya di sini seperti bool, len, dan sebagainya.

Ini sama cepatnya dengan peta (bool, ...)

Ivo van der Wijk
sumber
5
Ini sebenarnya idiom python. Ini juga satu-satunya saat saya masih menggunakan filter (), pemahaman daftar telah mengambil alih di tempat lain.
kaleissin
24
>>> lstr = ['hello', '', ' ', 'world', ' ']
>>> lstr
['hello', '', ' ', 'world', ' ']

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

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

Bandingkan waktu

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

Perhatikan bahwa filter(None, lstr)tidak menghapus string kosong dengan spasi ' ', hanya memangkas ''sementara ' '.join(lstr).split()menghilangkan keduanya.

Untuk digunakan filter()dengan string spasi putih dihapus, dibutuhkan lebih banyak waktu:

>>> timeit('filter(None, [l.replace(" ", "") for l in lstr])', "lstr=['hello', '', ' ', 'world', ' ']", number=10000000)
18.101892948150635
Aziz Alto
sumber
itu tidak akan berfungsi jika Anda memiliki ruang di antara untaian kata. misalnya: ['hello world', '', 'hello', '']. >> ['helloworld', '', 'hello', ''] apakah Anda punya solusi lain untuk menjaga jarak di dalam suatu item dalam daftar tetapi menghapus yang lain?
Reihan_amn
Perhatikan bahwa filter(None, lstr)tidak menghapus string kosong dengan spasi' ' Ya, karena itu bukan string kosong.
AMC
15

Balas dari @ Ib33X mengagumkan. Jika Anda ingin menghapus setiap string kosong, setelah dilucuti. Anda perlu menggunakan metode strip juga. Jika tidak, itu akan mengembalikan string kosong juga jika memiliki spasi putih. Seperti, "" juga akan valid untuk jawaban itu. Jadi, bisa diraih oleh.

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

Jawaban untuk ini adalah ["first", "second"].
Jika Anda ingin menggunakan filtermetode, Anda bisa melakukannya
list(filter(lambda item: item.strip(), strings)). Ini memberikan hasil yang sama.

ssi-anik
sumber
12

Alih-alih jika x, saya akan menggunakan jika X! = '' Untuk menghilangkan string kosong. Seperti ini:

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

Ini akan mempertahankan tipe data Tidak Ada dalam daftar Anda. Juga, jika daftar Anda memiliki bilangan bulat dan 0 adalah satu di antaranya, itu juga akan dipertahankan.

Sebagai contoh,

str_list = [None, '', 0, "Hi", '', "Hello"]
[x for x in str_list if x != '']
[None, 0, "Hi", "Hello"]
thiruvenkadam
sumber
2
Jika daftar Anda memiliki jenis yang berbeda (kecuali Tidak Ada), Anda mungkin memiliki masalah yang lebih besar.
Tritium21
Jenis apa? Saya mencoba dengan int dan tipe numerik lainnya, string, daftar, tupes, set, dan None dan tidak ada masalah di sana. Saya bisa melihat bahwa jika ada tipe yang ditentukan pengguna yang tidak mendukung metode str mungkin memberikan masalah. Haruskah saya khawatir tentang yang lain?
thiruvenkadam
1
Jika Anda memiliki str_list = [None, '', 0, "Hi", '', "Hello"], itu adalah tanda aplikasi yang dirancang dengan buruk. Anda seharusnya tidak memiliki lebih dari satu antarmuka (tipe) dan Tidak Ada dalam daftar yang sama.
Tritium21
3
Mengambil data dari db? daftar argumen untuk suatu fungsi saat melakukan pengujian otomatis?
thiruvenkadam
3
Itu biasanya tupel.
Tritium21
7

Bergantung pada ukuran daftar Anda, mungkin paling efisien jika Anda menggunakan list.remove () daripada membuat daftar baru:

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

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

Ini memiliki keuntungan karena tidak membuat daftar baru, tetapi kerugian karena harus mencari dari awal setiap kali, meskipun tidak seperti menggunakan while '' in lseperti yang diusulkan di atas, itu hanya membutuhkan pencarian sekali per kejadian ''(pasti ada cara untuk menjaga yang terbaik dari kedua metode, tetapi lebih rumit).

Andrew Jaffe
sumber
1
Anda dapat mengedit daftar di tempat dengan melakukan ary[:] = [e for e in ary if e]. Jauh lebih bersih dan tidak menggunakan pengecualian untuk aliran kontrol.
Krzysztof Karski
2
Yah, itu tidak benar-benar "di tempat" - Saya cukup yakin ini membuat daftar baru dan hanya menugaskannya ke nama yang lama.
Andrew Jaffe
Ini berkinerja sangat buruk karena ujung data dikocok di memori pada setiap pemindahan. Lebih baik menghapus semua dalam satu pukulan.
wim
7

Ingatlah bahwa jika Anda ingin menjaga spasi putih di dalam string , Anda dapat menghapusnya secara tidak sengaja menggunakan beberapa pendekatan. Jika Anda memiliki daftar ini

['hello world', '', '', 'hello'] apa yang Anda inginkan ['hello world', 'hello']

pertama potong daftar untuk mengkonversi semua jenis ruang putih menjadi string kosong:

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

kemudian hapus string kosong dari daftar mereka

space_clean_list = [x for x in space_to_empty if x]
Reihan_amn
sumber
jika Anda ingin menyimpan spasi putih di dalam string, Anda dapat menghapusnya secara tidak sengaja menggunakan beberapa pendekatan. Suka pendekatan ini?
AMC
Terima kasih Bung, itu berhasil untuk saya dengan sedikit perubahan. yaituspace_clean_list = [x.strip() for x in y if x.strip()]
Muhammad Mehran Khan Attari
6

Gunakan filter:

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

Kelemahan dari menggunakan filter sebagaimana ditunjukkan adalah bahwa ia lebih lambat daripada alternatif; juga, lambdabiasanya mahal.

Atau Anda bisa menggunakan yang paling sederhana dan paling berulang-ulang:

# 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

ini adalah metode yang paling intuitif dan melakukannya dalam waktu yang layak.

Aamir Mushtaq
sumber
9
Selamat datang di SO. Anda belum diabaikan. Anda belum pernah diserang oleh seorang downvoter yang tidak bersalah. Anda telah diberi umpan balik. Memperkuat: Arg pertama yang Anda usulkan untuk filter lebih buruk daripada lambda x: len(x)yang lebih buruk daripada lambda x : xyang terburuk dari 4 solusi dalam jawaban yang dipilih. Fungsi yang benar lebih disukai, tetapi tidak cukup. Arahkan kursor Anda ke tombol downvote: berbunyi "Jawaban ini tidak berguna".
John Machin
5

Seperti dilansir Aziz Alto filter(None, lstr) tidak menghapus string kosong dengan spasi ' 'tetapi jika Anda yakin lstr hanya berisi string yang dapat Anda gunakanfilter(str.strip, lstr)

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

Bandingkan waktu di pc saya

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

Solusi tercepat untuk menghapus ''dan mengosongkan string dengan spasi ' 'tetap ada' '.join(lstr).split() .

Seperti yang dilaporkan dalam komentar situasinya berbeda jika string Anda berisi spasi.

>>> 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']

Anda dapat melihat bahwa filter(str.strip, lstr)menyimpan string dengan spasi di atasnya tetapi ' '.join(lstr).split()akan membagi string ini.

Paolo Melchiorre
sumber
1
Ini hanya berfungsi jika string Anda tidak mengandung spasi. Jika tidak, Anda akan memisahkan string itu juga.
phillyslick
1
@ BenPolinsky seperti yang Anda laporkan joinsolusi akan memisahkan string dengan spasi tetapi filter tidak akan. Terima kasih atas komentar Anda, saya meningkatkan jawaban saya.
Paolo Melchiorre
-1

Ringkas jawaban terbaik:

1. Menghilangkan emtpties TANPA stripping:

Artinya, string semua ruang dipertahankan:

slist = list(filter(None, slist))

PRO:

  • paling sederhana;
  • tercepat (lihat tolok ukur di bawah).

2. Untuk menghilangkan kekosongan setelah pengupasan ...

2.a ... saat string TIDAK mengandung spasi di antara kata-kata:

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

PRO:

  • kode kecil
  • cepat (TAPI tidak tercepat dengan dataset besar karena memori, bertentangan dengan apa yang hasil @ paolo-melchiorre)

2.b ... kapan string berisi spasi antar kata?

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

PRO:

  • tercepat;
  • pemahaman kode.

Patokan pada mesin 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)
ankostis
sumber
s and s.strip()dapat disederhanakan menjadi adil s.strip().
AMC
s and s.strip()diperlukan jika kita ingin mereplikasi sepenuhnya filter(None, words), jawaban yang diterima. Saya mengoreksi fungsi sampel x2 di atas dan menjatuhkan x2 yang buruk.
ankostis
-2

Untuk daftar dengan kombinasi spasi dan nilai kosong, gunakan pemahaman daftar sederhana -

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

Jadi, Anda bisa lihat, daftar ini memiliki kombinasi spasi dan elemen nol. Menggunakan cuplikan -

>>> d = [x for x in s if x.strip()]
>>> d
>>> d = ['I', 'am', 'a', 'great', 'person', '!!', 'Do', 'you', 'think', 'its', 'a', 'a', 'joke', '?', '?']
Scid
sumber