Last active
December 12, 2018 01:18
-
-
Save mpettis/8a34a1dbc0f2f07e65352138c5b45790 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Dealing with NULLs as list values | |
library(tidyverse) | |
# As advertised on the tin: | |
is.null(NULL) | |
#> [1] TRUE | |
# is.null() is not vectorized? | |
is.null(list(NULL)) | |
#> [1] FALSE | |
# And you can't vectorize it over a list | |
Vectorize(is.null)(list(NULL, NULL)) | |
#> [1] FALSE | |
# But it can be mapped over | |
list(NULL, NULL) %>% map_lgl(is.null) | |
#> [1] TRUE TRUE | |
# Make a nested list, with some nulls | |
lstt <- list(parms = list(a=1, b=NULL, c="LETTER"), b=2, c=NULL, d="four") | |
# nulls are kept as entries | |
lstt %>% str() | |
#> List of 4 | |
#> $ parms:List of 3 | |
#> ..$ a: num 1 | |
#> ..$ b: NULL | |
#> ..$ c: chr "LETTER" | |
#> $ b : num 2 | |
#> $ c : NULL | |
#> $ d : chr "four" | |
# But I lose nulls when I unlist them | |
unlist(lstt) | |
#> parms.a parms.c b d | |
#> "1" "LETTER" "2" "four" | |
# I can recurse over list if I know what I am doing... | |
rapply(lstt, is.numeric) # All FALSE, because again, NULLS lost | |
#> parms.a parms.c b d | |
#> TRUE FALSE TRUE FALSE | |
rapply(lstt, function(x) x) # Identity function, still loses nulls | |
#> parms.a parms.c b d | |
#> "1" "LETTER" "2" "four" | |
# Alas, `rapply` can't be coerced to do this stuff. | |
# Here is a solution: | |
# https://stackoverflow.com/questions/38950005/how-to-manipulate-null-elements-in-a-nested-list | |
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(lstt, function(x) if (is.null(x)) NA else x) %>% | |
unlist() | |
#> parms.a parms.b parms.c b c d | |
#> "1" NA "LETTER" "2" NA "four" | |
# And added bonus, flattening list and returning data frame | |
replaceInList(lstt, function(x) if (is.null(x)) NA else x) %>% | |
unlist() %>% | |
enframe() %>% | |
spread(name, value) | |
#> # A tibble: 1 x 6 | |
#> b c d parms.a parms.b parms.c | |
#> <chr> <chr> <chr> <chr> <chr> <chr> | |
#> 1 2 <NA> four 1 <NA> LETTER |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment