dat=list(list(id = "T"), list(id = "T"),
structure(list(NULL), names = NA_character_), list(id = "T"),
list(id = "T"), list(id = "T"))
unlist(dat)
I treid but did not work
dat[sapply(dat, is.null)] <- NA
>Solution :
You should use the answer by ThomasIsCoding with a list as above, where you do not want to recursively unlist.
However, if it is possible you will have NULL at the top level, or if you want to unlist every level, then you need to traverse the levels of the list, setting NULL to NA.
tidyverse approach
I like to use purrr::modify_tree() for operations like this, passing a function to apply to every leaf.
dat |>
purrr::modify_tree(
leaf = \(x) if(is.null(x)) NA else x,
post = unlist
)
# id id <NA> id id id
# "T" "T" NA "T" "T" "T"
The post parameter is a function that is applied on the way "up", i.e. after the leaves are transformed.
base R approach
It feels as if you ought to be able to use base::rapply() to recursively iterate. Unfortunately, it ignores NULL elements. We can write a recursive function:
replace_null <- function(x, repl = NA) {
if(is.list(x)) return(lapply(x, replace_null))
if(is.null(x)) repl else x # or in R 4.4: x %||% repl
}
unlist(replace_null(dat))
# id id <NA> id id id
# "T" "T" NA "T" "T" "T"
The null coalescing operator %||%
Incidentally if you’re using the current development version of R (to be released as 4.4) you can use the null coalescing operator.
L %||% Rnewly in base is an expressive idiom for the phrasesif(!is.null(L)) L else Rorif(is.null(L)) R else L.
Here is an example of the difference of this approach when one of the top-level items is NULL.
l <- list(1, NULL)
unlist(l)
# [1] 1
dat |>
purrr::modify_tree(
leaf = \(x) x %||% NA,
post = unlist
)
# [1] 1 NA