I have a nested list containing NULL elements, and I\'d like to replace those with something else. For example:
l <- list(
NULL,
1,
list(
2,
I wrapped the replacement inside the sapply, which makes it more readable/understandable to me, albeit less general.
replace_null <- function(x) {
lapply(x, function(x) {
if (is.list(x)){
replace_null(x)
} else{
if(is.null(x)) NA else(x)
}
})
}
replace_null(l)
This can also be done with rrapply()
in the rrapply
-package. Below are a few different ways we could replace the NULL
elements in a nested list by NA
values:
library(rrapply)
l <- list(
NULL,
1,
list(
2,
NULL,
list(
3,
NULL
)
)
)
## replace NULL by NA using only f
rrapply(l, f = function(x) if(is.null(x)) NA else x, how = "replace")
#> [[1]]
#> [1] NA
#>
#> [[2]]
#> [1] 1
#>
#> [[3]]
#> [[3]][[1]]
#> [1] 2
#>
#> [[3]][[2]]
#> [1] NA
#>
#> [[3]][[3]]
#> [[3]][[3]][[1]]
#> [1] 3
#>
#> [[3]][[3]][[2]]
#> [1] NA
## replace NULL by NA using condition argument
rrapply(l, condition = is.null, f = function(x) NA, how = "replace")
#> [[1]]
#> [1] NA
#>
#> [[2]]
#> [1] 1
#>
#> [[3]]
#> [[3]][[1]]
#> [1] 2
#>
#> [[3]][[2]]
#> [1] NA
#>
#> [[3]][[3]]
#> [[3]][[3]][[1]]
#> [1] 3
#>
#> [[3]][[3]][[2]]
#> [1] NA
## replace NULL by NA using condition and deflt arguments
rrapply(l, condition = Negate(is.null), deflt = NA, how = "list")
#> [[1]]
#> [1] NA
#>
#> [[2]]
#> [1] 1
#>
#> [[3]]
#> [[3]][[1]]
#> [1] 2
#>
#> [[3]][[2]]
#> [1] NA
#>
#> [[3]][[3]]
#> [[3]][[3]][[1]]
#> [1] 3
#>
#> [[3]][[3]][[2]]
#> [1] NA
We can also prune the NULL
elements from the list altogether by setting how = "prune"
:
## keep only non-NULL elements
rrapply(l, condition = Negate(is.null), how = "prune")
#> [[1]]
#> [1] 1
#>
#> [[2]]
#> [[2]][[1]]
#> [1] 2
#>
#> [[2]][[2]]
#> [[2]][[2]][[1]]
#> [1] 3
This is what William Dunlap suggested in 2010 when this question was asked on Rhelp:
replaceInList <- function (x, FUN, ...)
{
if (is.list(x)) {
for (i in seq_along(x)) {
x[i] <- list(replaceInList(x[[i]], FUN, ...))
}
x
}
else FUN(x, ...)
}
replaceInList(l, function(x)if(is.null(x))NA else x)
I'm going to go with "use a version of rapply
doesn't doesn't have weird behaviour with NULL
". This is the simplest implementation I can think of:
simple_rapply <- function(x, fn)
{
if(is.list(x))
{
lapply(x, simple_rapply, fn)
} else
{
fn(x)
}
}
(rawr::rapply2
, as mentioned in the comments by @rawr is a more sophisticated attempt.)
Now I can do the replacement using
simple_rapply(l, function(x) if(is.null(x)) NA else x)
This is a hack, but as far as hacks go, I think I'm somewhat happy with it.
lna <- eval(parse(text = gsub("NULL", "NA", deparse(l))))
str(lna)
#> List of 3
#> $ : logi NA
#> $ : num 1
#> $ :List of 3
#> ..$ : num 2
#> ..$ : logi NA
#> ..$ :List of 2
#> .. ..$ : num 3
#> .. ..$ : logi NA
Update:
If for some reason you needed "NULL"
as a character entry in the list (corner case, much?) you can still use the above hack since it replaces the contents of the string, not the quotes, thus it just requires another step
l2 <- list(
NULL,
1,
list(
2,
"NULL",
list(
3,
NULL
)
)
)
lna2 <- eval(parse(text = gsub("NULL", "NA", deparse(l2))))
lna2_2 <- eval(parse(text = gsub('\\"NA\\"', '\"NULL\"', deparse(lna2))))
str(lna2_2)
#> List of 3
#> $ : logi NA
#> $ : num 1
#> $ :List of 3
#> ..$ : num 2
#> ..$ : chr "NULL"
#> ..$ :List of 2
#> .. ..$ : num 3
#> .. ..$ : logi NA