I have a list that contains some other lists, but also contains additional objects that aren't lists such as data frames. I want to flatten this to a single list, but the usual advice (see How to flatten a list of lists? or converting a list of lists in a single list) of unlist(..., recursive = FALSE)
won't work because it operates on the data.frame
as well.
> d <- list(list(a = 1, b = 2), c = data.frame(1:4))
> d
[[1]]
[[1]]$a
[1] 1
[[1]]$b
[1] 2
$c
X1.4
1 1
2 2
3 3
4 4
> unlist(d, recursive = FALSE)
$a
[1] 1
$b
[1] 2
$c.X1.4
[1] 1 2 3 4
The expected result is that $c
will retain the same data.frame
structure.
The only solution I have so far is to add an additional layer of lists to all non-lists object before unlist
ing but its a very unelegant solution.
> unlist(lapply(d, function(x) if(!inherits(x, "list")) list(x) else x), recursive = FALSE)
$a
[1] 1
$b
[1] 2
$c
X1.4
1 1
2 2
3 3
4 4
This works, but does anyone have a more direct idea? (I'd greatly prefer a base R solution, not tidyverse.)
CodePudding user response:
Admittedly, not necessarily "elegant", but still:
library(rrapply)
c(rrapply(rrapply(d, classes = "list", how = "flatten"), how = "flatten"),
rrapply(d, f = as.data.frame, classes = "data.frame", how = "flatten"))
$a
[1] 1
$b
[1] 2
$c
X1.4
1 1
2 2
3 3
4 4
As @tmfmnk mentioned in the comments, in your particular case, even this would work:
rrapply(d, classes = c("numeric", "data.frame"), how = "flatten")
So you basically define what classes should not be flattened further. This particular solution I'd then indeed call "elegant".
CodePudding user response:
You can use a recursive function (inspired by this answer).
flattenMixed <- function(x) {
if (is.data.frame(x)) return(list(x))
if (!is.list(x)) return(x)
unlist(lapply(x, flattenMixed), FALSE)
}
flattenMixed(d)
$a
[1] 1
$b
[1] 2
$c
X1.4
1 1
2 2
3 3
4 4