Рекурсивное удаление NULL элементов списка списков - PullRequest
1 голос
/ 03 августа 2020

У меня есть именованный список списков, подобных этому:

x <- list(a=1, b=2, c=list(ca=1, cb=2, cc=NULL), d=NULL)

Я хочу удалить все NULL элементы, в том числе в подсписках. Ожидаемый результат не содержит x$d и x$c$cc.

Я пробовал purrr::compact(x), но он удаляется только из верхнего списка. Вопрос и ответы от R: удаление NULL-элементов из списка применимо только к верхнему списку.

Я пробовал rapply(x, purrr::compact), но он не возвращает список.

Ответы [ 2 ]

2 голосов
/ 03 августа 2020

Один из подходов - использовать rrapply в rrapply -пакете (расширение базы rapply):

library(rrapply)

x <- list(a=1, b=2, c=list(ca=1, cb=2, cc=NULL), d=NULL)

rrapply(x, condition = Negate(is.null), how = "prune")
#> $a
#> [1] 1
#> 
#> $b
#> [1] 2
#> 
#> $c
#> $c$ca
#> [1] 1
#> 
#> $c$cb
#> [1] 2

Время тестирования

Сравнение времени вычисления rrapply с функцией rlist list.clean для некоторых больших вложенных списков, я получил следующие результаты:

## recursively create nested list with dmax layers and 50% NULL elements
f <- function(len, d, dmax) {
  x <- vector(mode = "list", length = len)
  for(i in seq_along(x)) {
    if(d + 1 < dmax) {
      x[[i]] <- Recall(len, d + 1, dmax)
    } else {
      x[[i]] <- list(1, NULL)
    }
  }
  return(x)
}

## long shallow list (3 layers, total 5e5 nodes)
x_long <- f(len = 500, d = 1, dmax = 3)

microbenchmark::microbenchmark(
  rlist = rlist::list.clean(x_long, recursive = TRUE),
  rrapply = rrapply::rrapply(x_long, condition = Negate(is.null), how = "prune"),
  check = "equal",
  times = 5L
)
#> Unit: milliseconds
#>     expr       min        lq      mean    median        uq       max neval
#>    rlist 2331.4914 2343.3001 2438.9674 2441.3850 2512.3484 2566.3121     5
#>  rrapply  353.7169  393.0646  400.8198  399.7971  417.7235  439.7972     5

## deeply nested list (18 layers, total 2^18 nodes)
x_deep <- f(len = 2, d = 1, dmax = 18)

microbenchmark::microbenchmark(
  rlist = rlist::list.clean(x_deep, recursive = TRUE),
  rrapply = rrapply::rrapply(x_deep, condition = Negate(is.null), how = "prune"),
  check = "equal",
  times = 5L
)
#> Unit: milliseconds
#>     expr       min        lq      mean    median        uq       max neval
#>    rlist 2167.2946 2251.5203 2279.9963 2292.5045 2332.4432 2356.2188     5
#>  rrapply  268.9463  274.7437  325.9585  292.4559  354.1607  439.4857     5
1 голос
/ 03 августа 2020

Вы можете использовать rlist::list.clean(x, recursive = TRUE). Если вы изучите исходный код, вы увидите, как рекурсия реализована с помощью простого lapply l oop.

...