Rimuovi le stringhe vuote da un elenco di stringhe


684

Voglio rimuovere tutte le stringhe vuote da un elenco di stringhe in Python.

La mia idea è simile a questa:

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

Esiste un modo più pitone per farlo?


45
@Ivo, nessuna di queste affermazioni è vera. Non dovresti mai modificare un elenco che for x in liststai ripetendo usando Se stai usando un while loopallora va bene. il loop mostrato rimuoverà le stringhe vuote fino a quando non ci saranno più stringhe vuote e poi si fermerà. In realtà non avevo nemmeno guardato la domanda (solo il titolo) ma ho risposto con lo stesso loop esatto come una possibilità! Se non vuoi usare comprensioni o filtri per motivi di memoria, è una soluzione molto pitonica.
aaronasterling,

4
Ancora un punto molto valido per non cambiare mai l'elenco su cui stai ripetendo :)
Eduard Luca,

1
@EduardLuca se il punto di scorrere una lista è cambiarlo, allora è l'opposto di quello che dovresti fare. Devi solo stare attento a sapere che non causi un comportamento imprevisto nel farlo.
JFA

1
@EduardLuca, @JFA: Il punto è che NON sta iterando su nessun elenco. Lo farebbe se avesse scritto qualcosa nel modulo for var in list:, ma qui ha scritto while const in list:. che non sta ripetendo nulla. sta solo ripetendo lo stesso codice fino a quando una condizione è falsa.
Camion

Risposte:


1155

Vorrei usare 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 restituisce un iteratore da filter, quindi dovrebbe essere racchiuso in una chiamata alist()

str_list = list(filter(None, str_list))

11
Se sei che preme per la prestazione, itertools'ifilter è ancora più velocemente- >>> 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 Molto vero. Tuttavia, con i ifilterrisultati valutati pigramente, non in una volta sola, direi che per la maggior parte dei casi ifilterè meglio. Interessante che l'utilizzo filterè ancora più veloce di avvolgere un ifilterin un listperò.
Humphrey Bogart,

3
Se lo fai su un elenco di numeri, nota che anche gli zero verranno rimossi (nota: ho usato solo i primi 3 metodi), quindi avrai bisogno di un metodo alternativo.
SnoringFrog

2
Questo si concentra solo sulla velocità, non su quanto sia pitonica la soluzione (la domanda che è stata posta). Le comprensioni dell'elenco sono la soluzione pythonic e il filtro dovrebbe essere usato solo se la profilazione ha dimostrato che listcomp è un collo di bottiglia.
Tritium21,

3
@ whoever-menzioni-riguardo-o-implica-Python-3, ti preghiamo di modificare e aggiornare la risposta. Stavamo discutendo per Python 2 solo quando è stata posta questa domanda, anche Python 3 è stato rilasciato per quasi 2 anni. Ma aggiorna i risultati di Python 2 e 3.
livibetter

237

L'uso della comprensione di un elenco è il modo più pitonico:

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

Se l'elenco deve essere modificato sul posto, poiché esistono altri riferimenti che devono visualizzare i dati aggiornati, utilizzare un'assegnazione di sezione:

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

16
Mi piace questa soluzione perché è facilmente adattabile. Se avevo bisogno di rimuovere non solo le stringhe vuote, ma le stringhe che sono solo gli spazi bianchi, per esempio: [x for x in strings if x.strip()].
Bond

67

Il filtro ha in realtà un'opzione speciale per questo:

filter(None, sequence)

Filtrerà tutti gli elementi che valutano Falso. Non c'è bisogno di usare un vero callable qui come bool, len e così via.

È altrettanto veloce della mappa (bool, ...)


5
Questo è un idioma del pitone, in effetti. È anche l'unica volta che uso ancora filter (), le comprensioni dell'elenco hanno preso il sopravvento in qualsiasi altro luogo.
Kaleissin,

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

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

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

Confronta il tempo

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

Si noti che filter(None, lstr)non rimuove stringhe vuote con uno spazio ' ', si pota solo via ''mentre ' '.join(lstr).split()rimuove entrambi.

Per utilizzare filter()con le stringhe di spazio bianco rimosse, ci vuole molto più tempo:

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

non funzionerà se hai spazio tra la stringa di una parola. ad esempio: ['hello world', '', 'hello', '']. >> ['helloworld', '', 'hello', ''] hai qualche altra soluzione per mantenere gli spazi all'interno di un elemento nell'elenco ma rimuoverne altri?
Reihan_amn

Nota che filter(None, lstr)non rimuove le stringhe vuote con uno spazio' ' Sì, perché non è una stringa vuota.
AMC

15

La risposta di @ Ib33X è fantastica. Se vuoi rimuovere ogni stringa vuota, dopo averla rimossa. è necessario utilizzare anche il metodo strip. Altrimenti, restituirà anche la stringa vuota se ha spazi bianchi. Ad esempio, "" sarà valido anche per quella risposta. Quindi, può essere raggiunto da.

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

La risposta per questo sarà ["first", "second"].
Se vuoi usare filterinvece il metodo, puoi fare come
list(filter(lambda item: item.strip(), strings)). Questo è dare lo stesso risultato.


12

Invece di se x, userei se X! = '' Per eliminare solo le stringhe vuote. Come questo:

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

Ciò conserverà il tipo di dati Nessuno nell'elenco. Inoltre, nel caso in cui la tua lista abbia numeri interi e 0 sia uno tra questi, verrà anche conservato.

Per esempio,

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

2
Se i tuoi elenchi hanno tipi diversi (tranne Nessuno), potresti avere un problema più grande.
Tritium21,

Che tipi? Ho provato con int e altri tipi numerici, stringhe, elenchi, tupes, set e None e nessun problema lì. Ho potuto vedere che se ci sono tipi definiti dall'utente che non supportano il metodo str potrebbe dare un problema. Dovrei essere preoccupato per qualcun altro?
thiruvenkadam,

1
Se hai un str_list = [None, '', 0, "Hi", '', "Hello"], è un segno di un'applicazione mal progettata. Non dovresti avere più di un'interfaccia (tipo) e Nessuno nella stessa lista.
Tritium21,

3
Recupero dati da db? elenco di argomenti per una funzione durante l'esecuzione di test automatizzati?
thiruvenkadam,

3
Quelle di solito sono tuple.
Tritium21,

7

A seconda delle dimensioni del tuo elenco, potrebbe essere più efficace se usi list.remove () anziché creare un nuovo elenco:

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

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

Questo ha il vantaggio di non creare un nuovo elenco, ma lo svantaggio di dover cercare dall'inizio ogni volta, anche se a differenza di while '' in lcome proposto sopra, richiede solo una ricerca per occorrenza di ''(c'è sicuramente un modo per mantenere il meglio di entrambi i metodi, ma è più complicato).


1
È possibile modificare l'elenco sul posto facendo ary[:] = [e for e in ary if e]. Molto più pulito e non utilizza eccezioni per il flusso di controllo.
Krzysztof Karski,

2
Beh, non è proprio "a posto" - Sono abbastanza sicuro che questo crei un nuovo elenco e lo assegni semplicemente al nome del vecchio.
Andrew Jaffe,

Ciò si comporta in modo molto scadente poiché la coda dei dati viene rimescolata in memoria ad ogni rimozione. Meglio rimuovere tutto in un colpo.
mercoledì

7

Tieni presente che se vuoi mantenere gli spazi bianchi all'interno di una stringa , puoi rimuoverli involontariamente usando alcuni approcci. Se hai questo elenco

['ciao mondo', '', '', 'ciao'] cosa potresti desiderare ['ciao mondo', 'ciao']

tagliare prima l'elenco per convertire qualsiasi tipo di spazio bianco in stringa vuota:

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

quindi rimuovere la stringa vuota dall'elenco

space_clean_list = [x for x in space_to_empty if x]

se si desidera mantenere gli spazi bianchi all'interno di una stringa, è possibile rimuoverli involontariamente utilizzando alcuni approcci. Ti piace questo approccio, allora?
AMC

Grazie amico, ha funzionato per me con un piccolo cambiamento. vale a direspace_clean_list = [x.strip() for x in y if x.strip()]
Muhammad Mehran Khan Attari il

6

Utilizzare filter:

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

Gli svantaggi dell'utilizzo del filtro come sottolineato è che è più lento delle alternative; inoltre, di lambdasolito è costoso.

Oppure puoi scegliere il più semplice e il più iterativo di tutti:

# 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

questo è il metodo più intuitivo e lo fa in tempo decente.


9
Benvenuti in SO. Non sei stato ignorato. Non sei stato attaccato da nessun downvoter. Ti è stato dato un feedback. Amplificazione: il tuo primo argomento proposto per il filtro è peggiore di quello lambda x: len(x)che è peggio di lambda x : xquale sia la peggiore delle 4 soluzioni nella risposta selezionata. Il funzionamento corretto è preferito, ma non sufficiente. Passa il cursore sopra il pulsante downvote: dice "Questa risposta non è utile".
John Machin,

5

Come riportato da Aziz Alto filter(None, lstr) non rimuove le stringhe vuote con uno spazio ' 'ma se sei sicuro che lstr contenga solo stringhe puoi usarefilter(str.strip, lstr)

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

Confronta i tempi sul mio pc

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

Rimane la soluzione più veloce per rimuovere ''e svuotare le stringhe con uno spazio .' '' '.join(lstr).split()

Come riportato in un commento, la situazione è diversa se le stringhe contengono spazi.

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

Puoi vedere che filter(str.strip, lstr)preserva le stringhe con degli spazi, ma ' '.join(lstr).split()le suddividerà.


1
Funziona solo se le tue stringhe non contengono spazi. Altrimenti, stai anche dividendo quelle stringhe.
phillyslick,

1
@BenPolinsky come hai segnalato la joinsoluzione dividerà le stringhe con spazio ma il filtro no. Grazie per il tuo commento, ho migliorato la mia risposta.
Paolo Melchiorre,

-1

Riassumi le migliori risposte:

1. Elimina i vuoti SENZA stripping:

Cioè, le stringhe per tutti gli spazi vengono mantenute:

slist = list(filter(None, slist))

Professionisti:

  • più semplice;
  • più veloce (vedere i benchmark di seguito).

2. Per eliminare i vuoti dopo lo stripping ...

2.a ... quando le stringhe NON contengono spazi tra le parole:

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

Professionisti:

  • piccolo codice
  • veloce (MA non più veloce con grandi set di dati a causa della memoria, contrariamente a quanto risulta da @ paolo-melchiorre)

2.b ... quando le stringhe contengono spazi tra le parole?

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

Professionisti:

  • più veloce;
  • comprensibilità del codice.

Benchmark su una macchina 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()può essere semplificato a solo s.strip().
AMC

s and s.strip()è necessario se vogliamo replicare completamente filter(None, words), la risposta accettata. Ho corretto le funzioni di esempio x2 sopra e lasciato cadere quelle cattive x2.
ankostis il

-2

Per un elenco con una combinazione di spazi e valori vuoti, usa la semplice comprensione dell'elenco -

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

Quindi, puoi vedere, questo elenco ha una combinazione di spazi ed elementi null. Utilizzo dello snippet -

>>> d = [x for x in s if x.strip()]
>>> d
>>> d = ['I', 'am', 'a', 'great', 'person', '!!', 'Do', 'you', 'think', 'its', 'a', 'a', 'joke', '?', '?']
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.