Eliminar cadenas vacías de una lista de cadenas


682

Quiero eliminar todas las cadenas vacías de una lista de cadenas en python.

Mi idea se ve así:

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

¿Hay alguna forma más pitónica de hacer esto?


45
@Ivo, ninguna de esas declaraciones es cierta. Nunca debe modificar una lista sobre la que está iterando for x in listsi está usando una, while loopentonces está bien. el bucle demostrado eliminará cadenas vacías hasta que no haya más cadenas vacías y luego se detendrá. En realidad, ni siquiera había mirado la pregunta (solo el título), ¡pero respondí exactamente con el mismo bucle como una posibilidad! Si no desea utilizar comprensiones o filtros por el bien de la memoria, es una solución muy pitónica.
aaronasterling

44
Sigue siendo un punto muy válido para nunca cambiar la lista sobre la que estás iterando :)
Eduard Luca

1
@EduardLuca si el punto de iterar sobre una lista es cambiarla, entonces eso es lo contrario de lo que debe hacer. Solo debe tener cuidado de saber que no causa un comportamiento inesperado al hacerlo.
JFA

1
@EduardLuca, @JFA: El punto es que NO está iterando sobre ninguna lista. Lo haría si hubiera escrito algo en el formulario for var in list:, pero aquí, ha escrito while const in list:. que no está iterando sobre nada. solo está repitiendo el mismo código hasta que una condición sea falsa.
Camion

Respuestas:


1150

Yo usaría 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 devuelve un iterador de filter, por lo que debe incluirse en una llamada alist()

str_list = list(filter(None, str_list))

11
Si está tan presionado por el rendimiento, itertool'sifilter es aún más rápido— >>> 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

44
@cpburnz Muy cierto. Sin embargo, con los ifilterresultados evaluados perezosamente, no de una sola vez, diría que para la mayoría de los casos ifilteres mejor. Es interesante que el uso filtersea ​​aún más rápido que envolver un ifilteren un listpensamiento.
Humphrey Bogart

3
Si hace esto en una lista de números, tenga en cuenta que los ceros también se eliminarán (nota: solo utilicé los primeros 3 métodos), por lo que necesitará un método alternativo.
SnoringFrog

2
Esto se centra solo en la velocidad, no en lo pitónica que es la solución (la pregunta que se hizo). Las comprensiones de listas son la solución pitónica, y el filtro solo debe usarse si el perfil ha demostrado que listcomp es un cuello de botella.
Tritium21

3
@ quien-menciona-sobre-o-implica-Python-3, solo edite y actualice la respuesta. Solo estábamos discutiendo para Python 2 cuando se hizo esta pregunta, incluso Python 3 se lanzó casi 2 años. Pero actualice los resultados de Python 2 y 3.
livibetter

236

Usar una lista de comprensión es la forma más pitónica:

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

Si la lista debe modificarse en el lugar, porque hay otras referencias que deben ver los datos actualizados, utilice una asignación de segmento:

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

16
Me gusta esta solución porque es fácilmente adaptable. Si necesitaba eliminar no sólo las cadenas vacías, pero las cadenas que son sólo espacios en blanco, por ejemplo: [x for x in strings if x.strip()].
Bono

67

filtro en realidad tiene una opción especial para esto:

filter(None, sequence)

Filtrará todos los elementos que evalúen a False. No es necesario usar una llamada real aquí como bool, len, etc.

Es igual de rápido que el mapa (bool, ...)


55
Este es un idioma de pitón, de hecho. También es la única vez que todavía uso filter (), las comprensiones de listas se han apoderado de todos los demás.
kaleissin

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

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

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

Comparar tiempo

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

Tenga en cuenta que filter(None, lstr)no elimina las cadenas vacías con un espacio ' ', solo elimina ''mientras ' '.join(lstr).split()elimina las dos.

Para usar filter()con las cadenas de espacio en blanco eliminadas, lleva mucho más tiempo:

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

no funcionará si tienes espacio entre la cadena de una palabra. por ejemplo: ['hola mundo', '', 'hola', '']. >> ['helloworld', '', 'hello', ''] ¿tiene alguna otra solución para mantener espacios dentro de un elemento en la lista pero eliminando otros?
Reihan_amn

Tenga en cuenta que filter(None, lstr)no elimina cadenas vacías con un espacio' ' Sí, porque esa no es una cadena vacía.
AMC

15

La respuesta de @ Ib33X es increíble. Si desea eliminar cada cadena vacía, después de despojado. necesitas usar el método de tira también. De lo contrario, también devolverá la cadena vacía si tiene espacios en blanco. Me gusta, "" también será válido para esa respuesta. Entonces, se puede lograr por.

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

La respuesta para esto será ["first", "second"].
Si desea utilizar el filtermétodo en su lugar, puede hacer me gusta
list(filter(lambda item: item.strip(), strings)). Esto es dar el mismo resultado.


12

En lugar de if x, usaría if X! = '' Para eliminar cadenas vacías. Me gusta esto:

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

Esto conservará el tipo de datos Ninguno dentro de su lista. Además, en caso de que su lista tenga enteros y 0 sea uno de ellos, también se conservará.

Por ejemplo,

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

2
Si sus listas tienen tipos dispares (excepto Ninguno), puede tener un problema mayor.
Tritium21

¿Qué tipos? Intenté con int y otros tipos numéricos, cadenas, listas, tupes, conjuntos y Ninguno y sin problemas. Pude ver que si hay algún tipo definido por el usuario que no sea compatible con el método str podría dar un problema. ¿Debería estar preocupado por cualquier otro?
thiruvenkadam

1
Si tiene una str_list = [None, '', 0, "Hi", '', "Hello"], es un signo de una aplicación mal diseñada. No debe tener más de una interfaz (tipo) y Ninguna en la misma lista.
Tritium21

3
¿Recuperando datos de db? ¿Lista de argumentos para una función mientras se realizan pruebas automatizadas?
thiruvenkadam

3
Esas suelen ser tuplas.
Tritium21

7

Dependiendo del tamaño de su lista, puede ser más eficiente si usa list.remove () en lugar de crear una nueva lista:

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

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

Esto tiene la ventaja de no crear una nueva lista, pero la desventaja de tener que buscar desde el principio cada vez, aunque a diferencia de usar while '' in lcomo se propuso anteriormente, solo requiere buscar una vez por ocurrencia ''(ciertamente hay una manera de mantener lo mejor de ambos métodos, pero es más complicado).


1
Puede editar la lista en su lugar haciendo ary[:] = [e for e in ary if e]. Mucho más limpio y no utiliza excepciones para controlar el flujo.
Krzysztof Karski

2
Bueno, eso no está realmente "en su lugar". Estoy bastante seguro de que esto crea una nueva lista y simplemente la asigna al nombre del anterior.
Andrew Jaffe

Esto funciona muy mal ya que la cola de datos se baraja en la memoria en cada eliminación. Es mejor eliminar todo en un solo golpe.
wim

7

Tenga en cuenta que si desea mantener los espacios en blanco dentro de una cadena , puede eliminarlos involuntariamente utilizando algunos enfoques. Si tienes esta lista

['hola mundo', '', '', 'hola'] lo que quieras ['hola mundo', 'hola']

primero recorte la lista para convertir cualquier tipo de espacio en blanco en una cadena vacía:

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

luego elimine la cadena vacía de la lista

space_clean_list = [x for x in space_to_empty if x]

Si desea mantener los espacios en blanco dentro de una cadena, puede eliminarlos involuntariamente utilizando algunos enfoques. ¿Te gusta este enfoque, entonces?
AMC

Gracias amigo, funcionó para mí con un pequeño cambio. es decirspace_clean_list = [x.strip() for x in y if x.strip()]
Muhammad Mehran Khan Attari

6

Uso filter:

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

Los inconvenientes de usar el filtro como se señaló es que es más lento que las alternativas; además,lambda suele ser costoso.

O puede optar por el más simple y el más iterativo de todos:

# 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

Este es el más intuitivo de los métodos y lo hace en un tiempo decente.


99
Bienvenido a SO. No has sido ignorado. Usted no ha sido atacado por ningún votante no homónimo. Le han dado su opinión. Amplificación: Su primer argumento propuesto para filtro es peor que lambda x: len(x)cuál es peor que lambda x : xcuál es la peor de las 4 soluciones en la respuesta seleccionada. Se prefiere el funcionamiento correcto, pero no es suficiente. Pase el cursor sobre el botón de votación negativa: dice "Esta respuesta no es útil".
John Machin el

5

Según lo informado por Aziz Alto filter(None, lstr) , no elimina las cadenas vacías con un espacio, ' 'pero si está seguro de que lstr contiene solo cadenas, puede usarfilter(str.strip, lstr)

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

Comparar el tiempo en mi 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

La solución más rápida para eliminar ''y vaciar cadenas con un espacio ' 'permanece' '.join(lstr).split() .

Como se informó en un comentario, la situación es diferente si sus cadenas contienen espacios.

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

Puede ver que filter(str.strip, lstr)preserva las cadenas con espacios, pero ' '.join(lstr).split()dividirá estas cadenas.


1
Esto solo funciona si sus cadenas no contienen espacios. De lo contrario, también está dividiendo esas cadenas.
phillyslick

1
@BenPolinsky como informó la joinsolución dividirá las cadenas con espacio pero el filtro no lo hará. Gracias por tu comentario, mejoré mi respuesta.
Paolo Melchiorre

-1

Resuma las mejores respuestas:

1. Eliminar emtpties SIN pelar:

Es decir, las cadenas de todo el espacio se retienen:

slist = list(filter(None, slist))

PRO:

  • más simple
  • más rápido (ver puntos de referencia a continuación).

2. Para eliminar los vacíos después de pelar ...

2.a ... cuando las cadenas NO contienen espacios entre palabras:

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

PRO:

  • código pequeño
  • rápido (PERO no más rápido con grandes conjuntos de datos debido a la memoria, a diferencia de lo que resulta @paolo-melchiorre)

2.b ... cuando las cadenas contienen espacios entre palabras?

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

PRO:

  • lo más rápido;
  • Comprensibilidad del código.

Puntos de referencia en una máquina 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()se puede simplificar a solo s.strip().
AMC

s and s.strip()es necesario si queremos replicar completamente filter(None, words), la respuesta aceptada. Corregí las funciones de muestra x2 anteriores y eliminé las x2 malas.
ankostis

-2

Para una lista con una combinación de espacios y valores vacíos, use la comprensión simple de la lista:

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

Entonces, puede ver, esta lista tiene una combinación de espacios y elementos nulos. Usando el fragmento -

>>> d = [x for x in s if x.strip()]
>>> d
>>> d = ['I', 'am', 'a', 'great', 'person', '!!', 'Do', 'you', 'think', 'its', 'a', 'a', 'joke', '?', '?']
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.