2014-10-23 16 views
28

Come posso rimuovere gli elementi nulli da una lista di liste, come qui di seguito, in R:Rimuovere gli elementi NULL dalla lista di liste

lll <- list(list(NULL),list(1),list("a")) 

L'oggetto che voglio sarebbe simile:

lll <- list(list(1),list("a")) 

Ho visto una risposta simile qui: How can I remove an element from a list? ma non è stato in grado di estenderlo da semplici elenchi a un elenco di elenchi.

EDIT

inappropriato esempio di cui sopra da parte mia. Entrambe le risposte funzionano su un caso più semplice (sopra). Che cosa succede se la lista è come:

lll <- list(list(NULL),list(1,2,3),list("a","b","c")) 

Come arrivare:

lll <- list(list(1,2,3),list("a","b","c")) 
+0

Potrei accettare una delle soluzioni di seguito? – zx8754

risposta

5

Dal momento che si dispone di elenchi di liste, probabilmente è necessario eseguire l/sapply due volte, come:

lll[!sapply(lll,sapply,is.null)] 

#[[1]] 
#[[1]][[1]] 
#[1] 1 
# 
# 
#[[2]] 
#[[2]][[1]] 
#[1] "a" 
7

Per questo esempio particolare è possibile utilizzare anche unlist con l'argomento recursive.

lll[!sapply(unlist(lll, recursive=FALSE), is.null)] 
# [[1]] 
# [[1]][[1]] 
# [1] 1 
# 
# 
# [[2]] 
# [[2]][[1]] 
# [1] "a" 
21

Ecco un'opzione con Filter e Negate combinazione

Filter(Negate(function(x) is.null(unlist(x))), lll) 
# [[1]] 
# [[1]][[1]] 
# [1] 1 
# 
# 
# [[2]] 
# [[2]][[1]] 
# [1] "a" 
+1

Sto lavorando con elenchi di oggetti S4 e questa soluzione è stata l'unica che ha funzionato, grazie! – Rafael

23

Questa soluzione ricorsiva ha la virtù di lavorare su ancora più in profondità liste nidificate.

È strettamente modellato sulla risposta di Gabor Grothendieck allo this quite similar question. La mia modifica di quel codice è necessaria se la funzione deve anche rimuovere oggetti come list(NULL) (non lo stesso di NULL), come si desidera.

## A helper function that tests whether an object is either NULL _or_ 
## a list of NULLs 
is.NullOb <- function(x) is.null(x) | all(sapply(x, is.null)) 

## Recursively step down into list, removing all such objects 
rmNullObs <- function(x) { 
    x <- Filter(Negate(is.NullOb), x) 
    lapply(x, function(x) if (is.list(x)) rmNullObs(x) else x) 
} 

rmNullObs(lll) 
# [[1]] 
# [[1]][[1]] 
# [1] 1 
# 
# 
# [[2]] 
# [[2]][[1]] 
# [1] "a" 

Ecco un esempio della sua applicazione ad un elenco più profondamente annidata, in cui le altre soluzioni attualmente proposte variamente sicuro.

LLLL <- list(lll) 
rmNullObs(LLLL) 
# [[1]] 
# [[1]][[1]] 
# [[1]][[1]][[1]] 
# [[1]][[1]][[1]][[1]] 
# [1] 1 
# 
# 
# [[1]][[1]][[2]] 
# [[1]][[1]][[2]][[1]] 
# [1] "a" 
+0

Questa è una grande funzione! – mrp

+1

@mrp - Grazie! Sono contento di essere riuscito a trasmettere questo modello utile a qualcun altro. –

+1

fyi, questo probabilmente si comporta in modi che non ci si potrebbe aspettare se i propri elementi di lista sono principalmente frame di dati, dal momento che 'is.list' catturerà e poi li scaricherà. Penso che aggiungere semplicemente un '&& is.data.frame (x)'. – joran

0

Soluzione rapida sulla soluzione di Josh O'Brien. C'è un po 'di un problema con gli elenchi delle funzioni

is.NullOb <- function(x) if(!(is.function(x))) is.null(x) | all(sapply(x, is.null)) else FALSE 

## Recursively step down into list, removing all such objects 
rmNullObs <- function(x) { 
    if(!(is.function(x))) { 
    x = x[!(sapply(x, is.NullOb))] 
    lapply(x, function(x) if (is.list(x)) rmNullObs(x) else x) 
    } 
} 
3

C'è una nuova rlist pacchetto su CRAN, grazie a Kun Ren per rendere la nostra vita più facile.

list.clean(.data, fun = is.null, recursive = FALSE) 

o per la rimozione ricorsiva di NULL:

list.clean(.data, fun = is.null, recursive = TRUE) 
4

Utilizzando purrr

purrr::map(lll, ~ purrr::compact(.)) %>% purrr::keep(~length(.) != 0) 
[[1]] 
[[1]][[1]] 
[1] 1 

[[1]][[2]] 
[1] 2 

[[1]][[3]] 
[1] 3 


[[2]] 
[[2]][[1]] 
[1] "a" 

[[2]][[2]] 
[1] "b" 

[[2]][[3]] 
[1] "c" 
Problemi correlati