Entfernen Sie leere Zeichenfolgen aus einer Liste von Zeichenfolgen

682

Ich möchte alle leeren Zeichenfolgen aus einer Liste von Zeichenfolgen in Python entfernen.

Meine Idee sieht so aus:

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

Gibt es eine pythonischere Möglichkeit, dies zu tun?

zerodx
quelle
45
@Ivo, keine dieser Aussagen ist wahr. Sie sollten niemals eine Liste ändern, über die for x in listSie iterieren. Wenn Sie eine verwenden, while loopist dies in Ordnung. Die gezeigte Schleife entfernt leere Zeichenfolgen, bis keine leeren Zeichenfolgen mehr vorhanden sind, und stoppt dann. Ich hatte mir die Frage (nur den Titel) nicht einmal angesehen, aber ich antwortete mit genau der gleichen Schleife wie möglich! Wenn Sie aus Gründen des Gedächtnisses keine Verständnisse oder Filter verwenden möchten, ist dies eine sehr pythonische Lösung.
Aaronasterling
4
Immer noch ein sehr gültiger Punkt, um die Liste, über die Sie iterieren, niemals zu ändern :)
Eduard Luca
1
@EduardLuca Wenn der Zweck des Durchlaufens einer Liste darin besteht, sie zu ändern, ist dies das Gegenteil von dem, was Sie tun sollten. Sie müssen nur darauf achten, dass Sie wissen, dass Sie dadurch kein unerwartetes Verhalten verursachen.
JFA
1
@EduardLuca, @JFA: Der Punkt ist, dass er KEINE Liste durchläuft. Er würde, wenn er etwas in der Form geschrieben hätte for var in list:, aber hier hat er geschrieben while const in list:. was nicht über irgendetwas iteriert. Es wird nur derselbe Code wiederholt, bis eine Bedingung falsch ist.
Camion

Antworten:

1150

Ich würde verwenden 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 gibt einen Iterator von zurück filterund sollte daher in einen Aufruf von eingeschlossen werdenlist()

str_list = list(filter(None, str_list))
livibetter
quelle
11
Wenn Sie , dass für die Leistung gedrückt, itertool‚sifilter ist auch 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.
Humphrey Bogart
4
@cpburnz Sehr wahr. Die ifilterErgebnisse werden jedoch träge und nicht auf einmal ausgewertet - ich würde argumentieren, dass dies in den meisten Fällen ifilterbesser ist. Interessant, dass die Verwendung filterimmer noch schneller ist als das Einwickeln eines ifilterIn list.
Humphrey Bogart
3
Wenn Sie dies mit einer Liste von Zahlen tun, beachten Sie, dass auch Nullen entfernt werden (Hinweis: Ich habe nur die ersten drei Methoden verwendet), sodass Sie eine alternative Methode benötigen.
Schnarchen Frosch
2
Dies konzentriert sich nur auf die Geschwindigkeit, nicht darauf, wie pythonisch die Lösung ist (die Frage, die gestellt wurde). Listenverständnisse sind die pythonische Lösung, und Filter sollten nur verwendet werden, wenn die Profilerstellung bewiesen hat, dass der Listencomp ein Engpass ist.
Tritium21
3
@ wer-erwähnt-über-oder-impliziert-Python-3, bitte bearbeiten und aktualisieren Sie einfach die Antwort. Wir haben nur für Python 2 diskutiert, als diese Frage gestellt wurde, sogar Python 3 wurde fast 2 Jahre veröffentlicht. Aktualisieren Sie jedoch die Ergebnisse von Python 2 und 3.
Livibetter
236

Die Verwendung eines Listenverständnisses ist der pythonischste Weg:

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

Wenn die Liste direkt geändert werden muss, da andere Referenzen die aktualisierten Daten enthalten müssen, verwenden Sie eine Slice-Zuweisung:

strings[:] = [x for x in strings if x]
Ib33X
quelle
16
Ich mag diese Lösung, weil sie leicht anpassbar ist. Wenn ich nicht nur leere Zeichenfolgen entfernen müsste, sondern auch Zeichenfolgen, die nur Leerzeichen sind, zum Beispiel : [x for x in strings if x.strip()].
Bond
67

Filter hat tatsächlich eine spezielle Option dafür:

filter(None, sequence)

Es werden alle Elemente herausgefiltert, die als falsch ausgewertet werden. Hier muss kein tatsächlicher Callable wie bool, len usw. verwendet werden.

Es ist genauso schnell wie eine Karte (bool, ...)

Ivo van der Wijk
quelle
5
Dies ist in der Tat eine Python-Sprache. Es ist auch das einzige Mal, dass ich filter () noch benutze, Listenverständnisse haben überall sonst übernommen.
Kaleissin
24
>>> lstr = ['hello', '', ' ', 'world', ' ']
>>> lstr
['hello', '', ' ', 'world', ' ']

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

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

Zeit vergleichen

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

Beachten Sie, dass filter(None, lstr)leere Zeichenfolgen nicht mit einem Leerzeichen entfernt werden ' ', sondern nur entfernt werden, ''während ' '.join(lstr).split()beide entfernt werden.

Die Verwendung filter()mit entfernten Leerzeichenstrings dauert viel länger:

>>> timeit('filter(None, [l.replace(" ", "") for l in lstr])', "lstr=['hello', '', ' ', 'world', ' ']", number=10000000)
18.101892948150635
Aziz Alto
quelle
Es funktioniert nicht, wenn Sie Platz in der Zeichenfolge eines Wortes haben. Zum Beispiel: ['Hallo Welt', '', 'Hallo', '']. >> ['helloworld', '', 'hallo', ''] Haben Sie eine andere Lösung, um Leerzeichen innerhalb eines Elements in der Liste beizubehalten, aber andere zu entfernen?
Reihan_amn
Beachten Sie, dass filter(None, lstr)leere Zeichenfolgen nicht mit einem Leerzeichen entfernt werden.' ' Ja, da dies keine leere Zeichenfolge ist.
AMC
15

Die Antwort von @ Ib33X ist fantastisch. Wenn Sie jede leere Zeichenfolge nach dem Entfernen entfernen möchten. Sie müssen auch die Strip-Methode verwenden. Andernfalls wird auch die leere Zeichenfolge zurückgegeben, wenn Leerzeichen vorhanden sind. Wie "" gilt auch für diese Antwort. So kann erreicht werden durch.

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

Die Antwort darauf wird sein ["first", "second"].
Wenn Sie filterstattdessen die Methode verwenden möchten , können Sie dies tun
list(filter(lambda item: item.strip(), strings)). Dies ergibt das gleiche Ergebnis.

ssi-anik
quelle
12

Anstelle von if x würde ich if X! = '' Verwenden, um nur leere Zeichenfolgen zu entfernen. So was:

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

Dadurch bleibt der Datentyp "Keine" in Ihrer Liste erhalten. Falls Ihre Liste Ganzzahlen enthält und 0 eine davon ist, bleibt sie ebenfalls erhalten.

Zum Beispiel,

str_list = [None, '', 0, "Hi", '', "Hello"]
[x for x in str_list if x != '']
[None, 0, "Hi", "Hello"]
Thiruvenkadam
quelle
2
Wenn Ihre Listen unterschiedliche Typen haben (außer Keine), liegt möglicherweise ein größeres Problem vor.
Tritium21
Welche Arten? Ich habe es mit int und anderen numerischen Typen, Strings, Listen, Tupes, Sets und None versucht und dort keine Probleme. Ich konnte sehen, dass wenn es benutzerdefinierte Typen gibt, die die str-Methode nicht unterstützen, dies ein Problem darstellen könnte. Sollte ich mir Sorgen um andere machen?
Thiruvenkadam
1
Wenn Sie eine haben str_list = [None, '', 0, "Hi", '', "Hello"], ist dies ein Zeichen für eine schlecht gestaltete Anwendung. Sie sollten nicht mehr als eine Schnittstelle (Typ) und Keine in derselben Liste haben.
Tritium21
3
Daten von db abrufen? Liste der Argumente für eine Funktion beim automatisierten Testen?
Thiruvenkadam
3
Das sind normalerweise Tupel.
Tritium21
7

Abhängig von der Größe Ihrer Liste ist es möglicherweise am effizientesten, wenn Sie list.remove () verwenden, anstatt eine neue Liste zu erstellen:

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

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

Dies hat den Vorteil, dass keine neue Liste erstellt wird, aber den Nachteil, dass jedes Mal von Anfang an gesucht werden muss, obwohl im Gegensatz while '' in lzur oben vorgeschlagenen Verwendung nur einmal pro Auftreten von gesucht werden muss ''(es gibt sicherlich eine Möglichkeit, das Beste zu behalten beide Methoden, aber es ist komplizierter).

Andrew Jaffe
quelle
1
Sie können die Liste an Ort und Stelle bearbeiten ary[:] = [e for e in ary if e]. Viel sauberer und verwendet keine Ausnahmen für den Kontrollfluss.
Krzysztof Karski
2
Nun, das ist nicht wirklich "an Ort und Stelle" - ich bin mir ziemlich sicher, dass dies eine neue Liste erstellt und sie einfach dem Namen des alten zuweist.
Andrew Jaffe
Dies funktioniert sehr schlecht, da das Ende der Daten bei jeder Entfernung im Speicher herumgemischt wird. Besser alles mit einem Schlag entfernen.
wim
7

Beachten Sie, dass Sie die Leerzeichen in einer Zeichenfolge möglicherweise unbeabsichtigt entfernen, indem Sie einige Ansätze verwenden. Wenn Sie diese Liste haben

['Hallo Welt', '', '', 'Hallo'] was du willst ['Hallo Welt', 'Hallo']

Schneiden Sie zuerst die Liste, um eine beliebige Art von Leerzeichen in eine leere Zeichenfolge umzuwandeln:

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

Entfernen Sie dann die leere Zeichenfolge aus der Liste

space_clean_list = [x for x in space_to_empty if x]
Reihan_amn
quelle
Wenn Sie die Leerzeichen innerhalb einer Zeichenfolge behalten möchten, können Sie sie mithilfe einiger Ansätze unbeabsichtigt entfernen. Wie dieser Ansatz dann?
AMC
Danke Alter, es hat bei mir mit einer kleinen Veränderung funktioniert. dhspace_clean_list = [x.strip() for x in y if x.strip()]
Muhammad Mehran Khan Attari
6

Verwendung filter:

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

Die Nachteile der Verwendung von Filtern bestehen darin, dass sie langsamer als Alternativen sind. Auch lambdaist in der Regel teuer.

Oder Sie können sich für das einfachste und iterativste von allen entscheiden:

# 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

Dies ist die intuitivste der Methoden und wird in angemessener Zeit durchgeführt.

Aamir Mushtaq
quelle
9
Willkommen bei SO. Sie wurden nicht ignoriert. Sie wurden nicht von einem nicht alltäglichen Downvoter angegriffen. Sie haben Feedback erhalten. Verstärkung: Ihr vorgeschlagenes erstes Argument für Filter ist schlechter als lambda x: len(x)das schlechtere als lambda x : xdas schlechteste der 4 Lösungen in der ausgewählten Antwort. Eine korrekte Funktion ist bevorzugt, aber nicht ausreichend. Bewegen Sie den Mauszeiger über die Downvote-Schaltfläche: "Diese Antwort ist nicht nützlich".
John Machin
5

Wie von Aziz Alto berichtet filter(None, lstr), werden leere Zeichenfolgen nicht mit einem Leerzeichen entfernt. ' 'Wenn Sie jedoch sicher sind, dass lstr nur Zeichenfolgen enthält, können Sie diese verwendenfilter(str.strip, lstr)

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

Vergleiche die Zeit auf meinem 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

Die schnellste Lösung zum Entfernen ''und Leeren von Zeichenfolgen mit einem Leerzeichen ' 'bleibt bestehen ' '.join(lstr).split().

Wie in einem Kommentar berichtet, ist die Situation anders, wenn Ihre Zeichenfolgen Leerzeichen enthalten.

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

Sie können sehen, dass filter(str.strip, lstr)Zeichenfolgen mit Leerzeichen beibehalten ' '.join(lstr).split()werden, diese Zeichenfolgen jedoch aufgeteilt werden.

Paolo Melchiorre
quelle
1
Dies funktioniert nur, wenn Ihre Zeichenfolgen keine Leerzeichen enthalten. Andernfalls teilen Sie diese Zeichenfolgen ebenfalls auf.
Phillyslick
1
@BenPolinsky, wie Sie die joinLösung gemeldet haben, teilt Zeichenfolgen mit Leerzeichen, Filter jedoch nicht. Vielen Dank für Ihren Kommentar. Ich habe meine Antwort verbessert.
Paolo Melchiorre
-1

Fassen Sie die besten Antworten zusammen:

1. Beseitigen Sie Leergut OHNE Abisolieren:

Das heißt, All-Space-Zeichenfolgen bleiben erhalten:

slist = list(filter(None, slist))

PROs:

  • am einfachsten;
  • am schnellsten (siehe Benchmarks unten).

2. Um Leergut nach dem Abisolieren zu beseitigen ...

2.a ... wenn Zeichenfolgen KEINE Leerzeichen zwischen Wörtern enthalten:

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

PROs:

  • kleiner Code
  • schnell (ABER nicht am schnellsten bei großen Datenmengen aufgrund des Speichers, im Gegensatz zu den Ergebnissen von @ paolo-melchiorre)

2.b ... wenn Strings Leerzeichen zwischen Wörtern enthalten?

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

PROs:

  • am schnellsten;
  • Verständlichkeit des Codes.

Benchmarks auf einer Maschine von 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
quelle
s and s.strip()kann einfach vereinfacht werden s.strip().
AMC
s and s.strip()wird benötigt, wenn wir filter(None, words)die akzeptierte Antwort vollständig replizieren wollen . Ich habe oben x2 Beispielfunktionen korrigiert und x2 schlechte Funktionen gelöscht.
Ankostis
-2

Verwenden Sie für eine Liste mit einer Kombination aus Leerzeichen und leeren Werten das einfache Listenverständnis -

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

Sie sehen also, diese Liste enthält eine Kombination aus Leerzeichen und Nullelementen. Verwenden des Snippets -

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