2010-10-02 22 views
445

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

Mi idea es el siguiente:

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

¿Hay alguna manera más Pythonic para hacer esto?

+5

usted debería * * Nunca modificar la lista que está interactuando sobre. Además, su bucle solo eliminará desde el inicio de su lista y se detendrá tan pronto como se haya activado una cadena que no esté vacía. –

+29

@ Ivo, ninguna de esas afirmaciones es verdadera. Nunca debe modificar una lista que itere usando 'for x in list' Si está utilizando' while loop', entonces está bien. el ciclo demostrado eliminará las cadenas vacías hasta que no haya más cadenas vacías y luego se detendrá. De hecho, ni siquiera había mirado la pregunta (solo el título) pero respondí con el mismo ciclo como una posibilidad. Si no quiere usar comprensiones o filtros por el bien de la memoria, es una solución muy pitonica. – aaronasterling

+5

@ AaronMcSmooth Tienes razón, hice suposiciones erróneas sobre el ciclo porque no me veía lo suficientemente bien. My bad –

Respuesta

749

I utilizaría filter:

str_list = filter(None, str_list) # fastest 
str_list = filter(bool, str_list) # fastest 
str_list = filter(len, str_list) # a bit slower 
str_list = filter(lambda item: item, str_list) # slower than list comprehension 

Python 3 devuelve un iterador de filter, por lo que debe ser envuelto en una llamada a list()

str_list = list(filter(None, str_list)) # fastest 

(etc.)

Pruebas:

>>> timeit('filter(None, str_list)', 'str_list=["a"]*1000', number=100000) 
2.4797441959381104 
>>> timeit('filter(bool, str_list)', 'str_list=["a"]*1000', number=100000) 
2.4788150787353516 
>>> timeit('filter(len, str_list)', 'str_list=["a"]*1000', number=100000) 
5.2126238346099854 
>>> timeit('[x for x in str_list if x]', 'str_list=["a"]*1000', number=100000) 
13.354584932327271 
>>> timeit('filter(lambda item: item, str_list)', 'str_list=["a"]*1000', number=100000) 
17.427681922912598 
+9

o 'filter (len, str_list)' –

+2

@Nick, pensé demasiado. ¿Qué hay de usar 'bool'? Hice una prueba rápida, es más rápido que 'len'. – livibetter

+0

@livibetter, ¡oh sí! ¿Por qué no editas tu respuesta para incluirla? –

136

List comprehensions

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

Salida: ['first', 'second']

Editar: acortado como se sugiere

+37

Esta solución es x9 veces más lenta que '' filter (None, my_list) ''. – Kee

+13

@kee No importa si es más lento que el filtro(). las listas de comprensión son la solución pitonica. – Tritium21

+0

@ Tritium21 Creo que ** importa ** si desea un código eficiente, independientemente de los problemas pitónicos. –

-2

bucle a través de la lista de cadenas existentes y para verificar si una cadena vacía, si no es poblar vacía una nueva cadena liste con los valores no vacíos y luego reemplace la lista de cuerdas anterior con la nueva lista de cuerdas

3

Dependiendo del tamaño de su lista, puede ser más eficaz si se utiliza 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 l como se propuso anteriormente, solo requiere buscar una vez por ocurrencia de '' (sin duda hay una manera de mantener lo mejor de ambos métodos, pero es más complicado).

+0

Puede editar la lista en su lugar haciendo 'ary [:] = [e para e si ary e]'. Mucho más limpio y no usa excepciones para el flujo de control. –

53

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

filter(None, sequence) 

Se filtrará todos los elementos que se evalúan como falso. No es necesario utilizar un callable real aquí, como bool, len, etc.

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

+4

Esto 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 hecho cargo de todos los demás lugares. – kaleissin

+0

Esta es la misma respuesta que http://stackoverflow.com/a/3845453/1224827 – Blairg23

4

Uso filter:

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

Los inconvenientes de la utilización del filtro como se ha señalado es que es más lenta que otras alternativas; también, lambda suele ser costoso.

O usted puede ir para el más simple y el más iterativo de todo:

# 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 el tiempo decente.

+7

Bienvenido a SO. No has sido ignorado. Usted no ha sido atacado por ningún votante desamparado. Usted ha recibido retroalimentación. Amplificación: Su primera arg for filter propuesta es peor que 'lambda x: len (x)' que es peor que 'lambda x: x', que es la peor de las 4 soluciones en la respuesta seleccionada. Se prefiere el funcionamiento correcto, pero no es suficiente. Coloca el cursor sobre el botón downvote: dice "Esta respuesta no es útil". –

+7

... y no deberías usar el nombre de un builtin como 'list' como variable. –

8

En lugar de si x, usaría si X! = '' Para eliminar cadenas vacías. De esta manera:

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"] 
+1

Si sus listas tienen tipos dispares (excepto Ninguno), es posible que tenga un problema mayor. – Tritium21

+0

¿Qué tipos? Intenté con int y otros tipos numéricos, cadenas, listas, tupes, sets y None y no hay problemas allí. 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 preocuparme por otro? – thiruvenkadam

+1

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

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

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

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

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

en cuenta que filter(None, lstr) no quita las cadenas vacías con un espacio ' ', sólo ciruelas distancia '' mientras ' '.join(lstr).split() elimina ambos.

Para utilizar filter() con las cadenas de espacios en blanco eliminados, se necesita mucho más tiempo:

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

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

+0

cuál no funcionará? –

+0

'' .join (lstr) .split() ¡Lo intenté! –

-2
str_list = ['2', '', '2', '', '2', '', '2', '', '2', ''] 

for item in str_list: 
    if len(item) < 1: 
     str_list.remove(item) 

corto y dulce.

-3

filter(None, str) no elimina cadenas vacías con un espacio '', solo elimina '' y ''.

join(str).split() quita ambos. pero si su elemento de la lista que tiene el espacio a continuación, que va a cambiar sus elementos de la lista también porque se adhiera por primera vez sus todos los elementos de la lista a continuación spiting ellos por espacio de lo que debe utilizar: -

str = ['hello', '', ' ', 'world', ' '] 
print filter(lambda x:x != '', filter(lambda x:x != ' ', str)) 

Se eliminarán los dos y ganado' t efectuar sus elementos también Al igual que: -

str = ['hello', '', ' ', 'world ram', ' '] 
print ' '.join(lstr).split() 
print filter(lambda x:x != '', filter(lambda x:x != ' ', lstr)) 

de salida: -

[ 'hola', 'mundo', 'carnero'] < ------------- - salida de ' '.join(lstr).split()
['hello', 'world ram']

7

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

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

La respuesta para esto será ["first", "second"].
En su lugar, si desea utilizar el método filter, puede hacerlo como
list(filter(lambda item: item.strip(), strings)). Esto es dar el mismo resultado.

+1

La mejor manera en mi opinión. Especialmente si hay cadenas en array en lugar de enteros. –

3

Según ha informado Aziz Altofilter(None, lstr) no elimina las cadenas vacías con un espacio ' ' pero si está seguro de LSTR sólo contiene cadena que puede utilizar filter(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 cadenas vacías con un espacio ' ' sigue siendo ' '.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'] 

Se puede ver que filter(str.strip, lstr) preservar cadenas con espacios en él, pero ' '.join(lstr).split() se repartirán esta cuerdas.

+1

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

+1

@BenPolinsky como informaste la solución 'join 'dividirá cadenas con espacio pero el filtro no lo hará. Gracias por tu comentario, mejoré mi respuesta. –

0

Para eliminar vacíos después de quitar:

slist = map(lambda s: s and s.strip(), slist) 
slist = filter(None, slist) 

algunas ventajas:

  • perezoso, sobre la base de los generadores, para ahorrar memoria;
  • comprensibilidad decente del código;
  • rápido, de forma selectiva mediante construcciones y comprensiones.

    def f1(slist): 
        slist = [s and s.strip() for s in slist] 
        return list(filter(None, slist)) 
    
    def f2(slist): 
        slist = [s and s.strip() for s in slist] 
        return [s for s in slist if s] 
    
    
    def f3(slist): 
        slist = map(lambda s: s and s.strip(), slist) 
        return list(filter(None, slist)) 
    
    def f4(slist): 
        slist = map(lambda s: s and s.strip(), slist) 
        return [s for s in slist if s] 
    
    %timeit f1(words) 
    10000 loops, best of 3: 106 µs per loop 
    
    %timeit f2(words) 
    10000 loops, best of 3: 126 µs per loop 
    
    %timeit f3(words) 
    10000 loops, best of 3: 165 µs per loop 
    
    %timeit f4(words) 
    10000 loops, best of 3: 169 µs per loop 
    
0

Tenga en cuenta que si desea mantener los espacios en blanco dentro de una cadena, es posible eliminarlos sin querer el uso de algunos enfoques. Si usted tiene esta lista

[ 'hola mundo', '', '', 'hola'] lo que es posible que desee [ 'hola mundo', 'hola']

recortar primero la lista de convertir cualquier tipo de espacio en blanco a cadena vacía:

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

luego retire cadena vacía lista de ellos

space_clean_list = [x for x in space_to_empty if x is not ""] 
Cuestiones relacionadas