web-dev-qa-db-fra.com

Supprimer des éléments NULL de la liste de listes

Comment puis-je supprimer les éléments nuls d'une liste de listes, comme ci-dessous, dans R:

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

L'objet que je veux ressemblerait à ceci:

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

J'ai vu une réponse similaire ici: Comment puis-je supprimer un élément d'une liste? mais n'a pas été en mesure de l'étendre de simples listes à une liste de listes.

MODIFIER

Mauvais exemple ci-dessus de ma part. Les deux réponses fonctionnent sur un cas plus simple (ci-dessus). Et si la liste est comme:

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

Comment avoir:

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

Cette solution récursive a le mérite de travailler sur des listes encore plus profondément imbriquées. 

Elle est étroitement inspirée de la réponse de Gabor Grothendieck à cette question assez similaire . Ma modification de ce code est nécessaire si la fonction doit également supprimer des objets tels que list(NULL) (différent de NULL), comme vous le souhaitez.

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

Voici un exemple de son application à une liste plus profondément imbriquée, sur laquelle les autres solutions actuellement proposées échouent différemment.

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"
26
Josh O'Brien

Voici une option utilisant les combinaisons Filter et Negate

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

Pour cet exemple particulier, vous pouvez également utiliser unlist avec son argument recursive.

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

Utiliser 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"
5
Leni Ohnesorge

Puisque vous avez des listes dans des listes, vous devrez probablement exécuter l/sapply deux fois, comme:

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

#[[1]]
#[[1]][[1]]
#[1] 1
#
#
#[[2]]
#[[2]][[1]]
#[1] "a"
5
thelatemail

Il y a un nouveau paquet rlist sur CRAN, merci à Kun Ren pour nous avoir simplifié la vie.

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

ou pour la suppression récursive de NULL:

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

Solution rapide à la solution de Josh O'Brien. Il y a un petit problème avec les listes de fonctions

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)
  }
}
0
bramtayl