2012-04-05 15 views
7

Esiste un modo elegante per far rimuovere gli elementi vuoti da una lista. Una sorta di list.rstrip (Nessuno). Cosìrimozione di elementi vuoti finali in Python

[1, 2, 3, None, 4, None, None] 

dovrebbe comportare

[1, 2, 3, None, 4] 

Credo che questo possa essere generalizzato per la rimozione di elementi di trascinamento di qualsiasi particolare valore.


UPDATE: mi avrebbe dovuto precisare che io voglio che questo fatto come una singola linea (leggibile) espressione

+2

Cosa c'è di sbagliato in un ciclo? Non tutte le fodere sono leggibili. Votazione per via dell'aggiornamento – Abhijit

+2

Ecco l'espressione a riga singola (leggibile): 'strip_trailing_empty_elements (the_list)'. Ti suggerisco di usare una delle risposte che ti sono state date per definire 'strip_trailing_empty_elements()', – Duncan

+1

Come ho detto, voglio uno "readable" leggibile (che è sempre più leggibile di un ciclo). Se non credi che ce ne siano, allora questa è la risposta alla mia domanda. Allora perché il downvote? –

risposta

14

A cura: Per utilizzare pop(), invece di tagliare la lista, come commentatori hanno correttamente suggerito.

Non so se c'è un modo più divinatorio, ma questo funziona:

my_list = [1, 2, 3, None, 4, None, None] 
while not my_list[-1]: 
    my_list.pop() 
print my_list 
>>>[1, 2, 3, None, 4] 

Edit: Come @DSM fa notare, se si voleva solo per sbarazzarsi di None valori, e lasciare zeri o altri valori falsy, si potrebbe fare:

while my_list and my_list[-1] is None: 
    my_list.pop() 
+1

Ciò rimuove anche gli zero finali (e qualsiasi altra cosa falsi). – DSM

+2

Ciò creerebbe copie non necessarie a ogni iterazione – Abhijit

+0

@DSM Abbastanza corretto. Modificato la risposta per tenerne conto. – alan

7

i seguenti controlli esplicitamente None elementi:

while l and l[-1] is None: 
    l.pop() 

Si può essere generalizzato come segue:

f = lambda x: x is None 
while l and f(l[-1]): 
    l.pop() 

È possibile ora definire le funzioni differenti per f per verificare la presenza di altre condizioni.

+0

si prega di consultare il mio aggiornamento –

+2

Buona soluzione, ma genera un'eccezione quando un elenco contiene solo Nones. Forse 'while l e f (l [-1])' – georg

+0

@ thg435: sì, hai ragione. Ho aggiunto la tua modifica al post, grazie. –

2

Prova questo

>>> x=[1, 2, 3, None, 4, None, None] 
>>> while x[-1] is None: 
    x.pop() 
+0

si prega di vedere il mio aggiornamento –

+0

la tua risposta è come la risposta di alan, è possibile aggiungere un voto alla sua risposta, invece di emettere una nuova risposta. – pylover

+2

@pylover: la risposta di alan crea copie non necessarie. Avrebbe dovuto usare pop invece. – Abhijit

1

Per una soluzione di una sola riga:

In [30]: from itertools import dropwhile 

In [31]: list(reversed(tuple(dropwhile(lambda x: x is None, reversed([1, 2, 3, None, 4, None, None]))))) 
Out[31]: [1, 2, 3, None, 4] 

Se si desidera riutilizzarlo, ecco una definizione in stile libero-point:

In [36]: from functional import compose, partial 

In [37]: varargs = lambda *args: args 

In [38]: compose_mult = compose(partial(reduce, compose),varargs) # compose which takes variable number of arguments. Innermost function to the right. 

In [39]: compose_mult(list, reversed, tuple, partial(dropwhile, lambda x: x is None), reversed)([1, 2, 3, None, 4, None, None]) 
Out[39]: [1, 2, 3, None, 4] 
+0

Funziona ma a mio parere sacrifica troppo la leggibilità. –

+0

@SimeonVisser Questa è una questione di opinione, ma sì, non mi fa impazzire la necessità di forzare una tupla nel mezzo. – Marcin

3
def remove_trailing(l, remove_value=None): 
     i = len(l) 
     while i>0 and l[i-1] == remove_value: 
      i -= 1 
     return l[:i] 
+0

Questa è la migliore risposta di gran lunga. Un'altra variazione sarebbe un'assegnazione di slice sul posto invece di restituire la slice per preservare la separazione di comando/query.Se ti piace. Questo è un bel stile funzionale. – aaronasterling

+0

@aaronasterling Mi piace anche questa soluzione. Sto cercando di saperne di più sullo stile funzionale. Cosa intendi con __in-place slice assignment__. Assegnare la fetta al parametro 'l'? E non restituirlo? (Sicuramente no) – alan

+0

@alan, invece di 'return l [: i]', sarebbe 'l [:] = l [: i]'. Questo lo rende un comando in quanto cambierebbe lo stato di 'l'. Ero fuori base nel fatto che è attualmente una query (restituisce i dati e non cambia stato) in modo da preservare la separazione delle query dei comandi. Stavo immaginando di usarlo come 'l = remove_trailing (l)' e _that_ violerebbe la separazione CQ, _imo_. Probabilmente sono troppo pedante qui. – aaronasterling

0

Ecco un altro one-liner:

>>> l = [1, 2, 3, None, 4, None, None] 
>>> [l[i] for i in range(len("".join(map(lambda x: {None: " "}.get(x, "_"), l)).rstrip()))] 
[1, 2, 3, None, 4] 

Queste cose sono divertenti!

EDIT

Ho appena realizzato che la lista di comprensione è completamente inutile. Slicing dovrebbe funzionare bene:

>>> l[:len("".join(map(lambda x: {None: " "}.get(x, "_"), l)).rstrip())] 
+0

E fa uso di 'rstrip()', anche se in maniera molto rotonda. –

1

Se davvero è necessario compromettere la leggibilità, ecco cosa farei.

>>> from itertools import takewhile 
>>> l=[1,2,3,None,4,5,None,None] 
>>> l[:-len(list(takewhile(lambda x: x==None, reversed(l))))] 
[1,2,3,None,4,5] 
+0

Appare dall'output che non si ottiene nulla. – Marcin

+0

Ho copiato la riga sbagliata, invece ho ottenuto il risultato corretto e scommetto più velocemente di molte altre soluzioni – luke14free

0

Che dire di ricorsione + affettatura?

>>> rstrip = lambda x, elts=(None,): rstrip(x[:-1], elts) if x and x[-1] in elts else x 
>>> rstrip([1, 2, 3, None]) 
[1, 2, 3] 
>>> rstrip([1, 2, 3, None], (None, 3)) 
[1, 2] 

Nota: Sto assumendo che non stai cercando la soluzione più efficiente computazionalmente qui ...

0

Che dire:

[a[i] for i in range(len(a)) if a[i:] != [None] * (len(a) - i) ] 
1

non ho mai accettato una risposta perché io non era davvero felice con le soluzioni offerte. Ecco un'altra soluzione (1 riga e senza dipendenze di libreria) che non sono del tutto soddisfatto con entrambi:

a = [1, 2, None, 3, None, None] 
reduce(lambda l, e: [e]+l if l or e is not None else [], reversed(a), []) 
+0

Sarebbe bello se esistesse https://docs.python.org/3.6/library/stdtypes.html#str.rstrip per liste Ma poiché non funziona, il ciclo pop a due righe di alan funziona per me. – Dave

0

Il progetto more_itertools implementa rstrip per qualsiasi iterabile:

iterable = [1, 2, 3, None, 4, None, None] 
list(mit.rstrip(iterable, lambda x: x in {None})) 
# [1, 2, 3, None, 4] 

more_itertools.rstrip accetta un iterabile e il predicato . Vedi lo source code per i dettagli.

Problemi correlati