Dizeler listesinden boş dizeleri kaldırma


684

Python dizeleri listesinden tüm boş dizeleri kaldırmak istiyorum.

Benim fikrim şöyle:

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

Bunu yapmanın daha pythonic bir yolu var mı?


45
@Ivo, bu ifadelerin hiçbiri doğru değil. Sen senin kullanarak yineleme bir listede değişiklik asla for x in listbir kullanıyorsanız while loopo zaman 's cezası. gösterilen döngü, boş dizge kalmayana kadar boş dizeleri kaldıracak ve sonra duracaktır. Aslında soruya bile bakmadım (sadece başlık) ama bir olasılıkla aynı döngü ile cevap verdim! Bellek için anlama veya filtreler kullanmak istemiyorsanız, bu çok pitonik bir çözümdür.
aaronasterling

4
Üzerinde yinelediğiniz listeyi asla değiştirmek için hala çok geçerli bir nokta :)
Eduard Luca

1
@EduardLuca bir liste üzerinde yineleme yapmanın amacı listeyi değiştirmekse, o zaman yapmanız gerekenin tam tersidir. Sadece bunu yaparak beklenmedik bir davranışa neden olmadığınızı bildiğinizden emin olmalısınız.
JFA

1
@EduardLuca, @JFA: Mesele şu ki, herhangi bir listeyi yinelemiyor. Formda bir şey yazmış olsaydı for var in list:, ama burada yazdı while const in list:. ki bu hiçbir şey üzerinde tekrar etmiyor. bir koşul yanlış olana kadar aynı kodu tekrarlar.
Camion

Yanıtlar:


1154

Kullanacağım 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 bir yineleyiciyi döndürür filter, bu nedenle bir çağrıya sarılmalıdırlist()

str_list = list(filter(None, str_list))

11
Eğer değilseniz o performans için bastırdı, itertool'ınifilter bile hızından hızlı olduğunu >>> 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 Çok doğru. Bununla birlikte, ifiltersonuçlar tembel olarak değerlendirilir, bir seferde değil - çoğu durumda ifilterdaha iyi olduğunu iddia ediyorum . İlginçtir ki , bir filteranı sarmaktan hala daha hızlıdır . ifilterlist
Humphrey Bogart

3
Bunu bir sayı listesine yaparsanız, sıfırların da kaldırılacağını unutmayın (not: Yalnızca ilk 3 yöntemi kullandım), bu yüzden alternatif bir yönteme ihtiyacınız olacak.
SnoringFrog

2
Bu, çözümün ne kadar pitonik olduğuna değil (sorulan soru) sadece hıza odaklanır. Liste Anlamaları, pythonic çözümdür ve filtre yalnızca profil oluşturma işleminin listcomp'ın bir darboğaz olduğunu kanıtlaması durumunda kullanılmalıdır.
Tritium21

3
@ kim-hakkında-veya-ima-Python-3 bahseder, lütfen sadece cevap düzenleyin ve güncelleyin. Python 2 için sadece bu soru sorulduğunda tartışıyorduk, hatta Python 3 neredeyse 2 yıl yayınlandı. Ancak hem Python 2 hem de 3 sonuçlarını güncelleyin.
livibetter

237

Bir liste kavraması kullanmak en Pythonic yöntemidir:

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

Listenin yerinde değiştirilmesi gerekiyorsa, güncellenmiş verileri görmesi gereken başka başvurular olduğundan, bir dilim ataması kullanın:

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

16
Bu çözümü seviyorum çünkü kolayca uyarlanabilir. Ben örneğin, boş dizeleri ama sadece boşluk vardır dizeleri sadece kaldırmak için gerekirse: [x for x in strings if x.strip()].
Bond

67

filtre aslında bunun için özel bir seçeneğe sahiptir:

filter(None, sequence)

Yanlış olarak değerlendirilen tüm öğeleri filtreleyecektir. Burada bool, len ve benzeri gerçek bir çağrılabilir kullanmaya gerek yoktur.

Harita kadar hızlı (bool, ...)


5
Bu aslında bir python deyimidir. Yine de filter () 'i kullandığım tek zamandır, liste kavrayışları başka her yere taşındı.
kaleissin

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

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

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

Zaman karşılaştır

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

Bildirim filter(None, lstr)bir boşlukla boş dizeleri kaldırmaz ' ', sadece uzakta kuru erik ''ise ' '.join(lstr).split()uzaklaşmaların hem.

filter()Beyaz boşluk dizeleri kaldırıldığında kullanmak çok daha fazla zaman alır:

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

bir kelimenin dizesi arasında boşluk varsa çalışmaz. örneğin: ['merhaba dünya', '', 'merhaba', '']. >> ['helloworld', '', 'merhaba', ''] listedeki bir öğenin içindeki boşlukları tutmak, ancak başkalarını kaldırmak için başka bir çözümünüz var mı?
Reihan_amn

filter(None, lstr)Boş dizeleri boşluk' ' bırakmadığına dikkat edin. Evet, çünkü bu boş bir dize değil.
AMC

15

@ Ib33X yanıt harika. Her boş dizeyi kaldırmak istiyorsanız, soyulduktan sonra. şerit yöntemini de kullanmanız gerekir. Aksi takdirde, beyaz boşlukları varsa boş dizeyi de döndürür. Mesela, "" bu cevap için de geçerli olacak. Böylece, tarafından elde edilebilir.

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

Bunun cevabı olacak ["first", "second"]. Bunun yerine yöntemi
kullanmak isterseniz filter, bunu yapabilirsiniz
list(filter(lambda item: item.strip(), strings)). Bu aynı sonucu verir.


12

Eğer x yerine, sadece boş dizeleri ortadan kaldırmak için X! = '' Kullanırım. Bunun gibi:

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

Bu, listenizdeki Hiçbiri veri türünü koruyacaktır. Ayrıca, listenizde tamsayıların olması ve bunların arasında 0 olması durumunda da korunacaktır.

Örneğin,

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

2
Listeleriniz farklı türlere sahipse (Hiçbiri hariç), daha büyük bir sorununuz olabilir.
Tritium21

Hangi türler? İnt ve diğer sayısal türleri, dizeleri, listeleri, tüpleri, setleri ve Hiçbiri ile çalıştı ve hiçbir sorun var. Str yöntemini desteklemeyen herhangi bir kullanıcı tanımlı tür varsa bir sorun verebilir görebilirsiniz. Başka biri için endişelenmeli miyim?
thiruvenkadam

1
Eğer varsa, str_list = [None, '', 0, "Hi", '', "Hello"]kötü tasarlanmış bir uygulamanın bir işaretidir. Sen olmamalıdır aynı listede birden fazla arayüz (tip) ve Hiçbiri.
Tritium21

3
Db'den veri alınıyor mu? test yaparken bir fonksiyon için argüman listesi?
thiruvenkadam

3
Bunlar genellikle tuple.
Tritium21

7

Listenizin boyutuna bağlı olarak, yeni bir liste oluşturmak yerine list.remove () yöntemini kullanırsanız daha etkili olabilir:

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

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

Bu, yeni bir liste oluşturmama avantajına sahiptir, ancak her seferinde en baştan aramak zorunda kalmanın dezavantajı, while '' in lyukarıda önerildiği gibi kullanılmamasına rağmen , yalnızca bir kez arama yapılmasını gerektirir ''( her iki yöntem de, ancak daha karmaşıktır).


1
Yaparak listeyi yerinde düzenleyebilirsiniz ary[:] = [e for e in ary if e]. Çok daha temiz ve kontrol akışı için istisnalar kullanmaz.
Krzysztof Karski

2
Eh, bu gerçekten "yerinde" değil - eminim bu yeni bir liste oluşturur ve sadece eskisinin adına atar.
Andrew Jaffe

Her kaldırmada veri kuyruğu karıştırıldığında bu çok kötü performans gösterir. Hepsini tek bir vuruşta kaldırmak daha iyi.
wim

7

Beyaz boşlukları bir dize içinde tutmak istiyorsanız, bazı yaklaşımları kullanarak bunları yanlışlıkla kaldırabileceğinizi unutmayın. Bu listeye sahipseniz

['merhaba dünya', '', '', 'merhaba'] ne isteyebilirsiniz ['merhaba dünya', 'merhaba']

önce beyaz boşluk türünü boş dizeye dönüştürmek için listeyi kırpın:

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

sonra boş dizeyi listeden kaldırın

space_clean_list = [x for x in space_to_empty if x]

beyaz boşlukları bir dize içinde tutmak istiyorsanız, bazı yaklaşımları kullanarak bunları yanlışlıkla kaldırabilirsiniz. Peki bu yaklaşım gibi mi?
AMC

Teşekkürler dostum, küçük bir değişiklikle benim için çalıştı. yanispace_clean_list = [x.strip() for x in y if x.strip()]
Muhammed Mehran Han Attari

6

Kullanım filter:

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

Filtrenin belirtildiği gibi kullanılmasının dezavantajları, alternatiflerden daha yavaş olmasıdır; Ayrıca,lambda genellikle maliyetlidir.

Veya en basit ve en yinelemeli için gidebilirsiniz:

# 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

bu yöntemlerin en sezgisel olanıdır ve bunu uygun zamanda yapar.


9
SO hoş geldiniz. Göz ardı edilmediniz. Anonim bir aşağı saldırgan tarafından saldırıya uğramadınız. Size geri bildirim verildi. Kuvvetlendirme: Filtre için önerilen ilk argümanınız , seçilen yanıttaki 4 çözümden en lambda x: len(x)kötüsü olandan daha kötüdür lambda x : x. Doğru işlem tercih edilir, ancak yeterli değildir. İmlecinizi aşağı oy düğmesinin üzerine getirin: "Bu cevap yararlı değil" yazıyor.
John Machin

5

Aziz Alto tarafından bildirildiği gibi filter(None, lstr)bir boşluk ile boş dizeleri kaldırmaz ' 'ama lstr sadece dize içerdiğinden eminseniz kullanabilirsinizfilter(str.strip, lstr)

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

Bilgisayarımdaki zamanı karşılaştır

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

''Dizeleri boşlukla kaldırmak ve boşaltmak için en hızlı çözüm ' 'kalır ' '.join(lstr).split().

Yorumda bildirildiği gibi, dizeleriniz boşluk içeriyorsa durum farklıdır.

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

Üzerinde filter(str.strip, lstr)boşluk bulunan dizeleri koruduğunuzu ancak ' '.join(lstr).split()bu dizeleri bölebileceğini görebilirsiniz.


1
Bu yalnızca dizeleriniz boşluk içermiyorsa çalışır. Aksi takdirde, bu dizeleri de bölüyorsunuz.
phillyslick

1
@BenPolinsky bildirdiğiniz gibi joinçözüm dizeleri boşlukla böler ama filtre olmaz. Yorumunuz için teşekkür ederim Cevabımı geliştirdim.
Paolo Melchiorre

-1

En iyi cevapları toplayın:

1. sıyırma OLMADAN emtpties ortadan kaldırmak:

Yani, tüm alan dizeleri korunur:

slist = list(filter(None, slist))

Artıları:

  • En basit;
  • en hızlı (aşağıdaki ölçütlere bakın).

2. Sıyırma işleminden sonra boşalmaları ortadan kaldırmak için ...

2.a ... dizeler kelimeler arasında boşluk İÇERMEDİĞİNDE:

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

Artıları:

  • küçük kod
  • hızlı (AMA @ paolo-melchiorre sonuçlarının aksine, bellek nedeniyle büyük veri kümeleriyle en hızlı değil)

2.b ... dizeler kelimeler arasında boşluk bıraktığında?

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

Artıları:

  • En hızlı;
  • kodun anlaşılırlığı.

2018 makinesindeki karşılaştırmalar:

## 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()sadece basitleştirilebilir s.strip().
AMC

s and s.strip()filter(None, words)kabul edersek, tam olarak çoğaltmak istiyorsak . Yukarıdaki x2 örnek işlevlerini düzelttim ve x2 kötü işlevlerini düşürdüm.
ankostis

-2

Boşlukların ve boş değerlerin birleşiminden oluşan bir liste için basit liste kavrayışı kullanın -

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

Gördüğünüz gibi, bu listede boşluklar ve boş elemanların bir kombinasyonu var. Parçacığı kullanma -

>>> d = [x for x in s if x.strip()]
>>> d
>>> d = ['I', 'am', 'a', 'great', 'person', '!!', 'Do', 'you', 'think', 'its', 'a', 'a', 'joke', '?', '?']
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.