Recursively flattens an R object. Unlike base::unlist()
, it
Usage
as_flat_list(
x,
is_node = NULL,
name_spec = "{outer}.{inner}",
name_repair = c("minimal", "unique", "check_unique", "universal")
)
Arguments
- x
An R object.
- is_node
A predicate function that determines whether an element is a node (by returning
TRUE
) or a leaf (by returningFALSE
). The default value,NULL
, treats simple lists as nodes and everything else (including richer objects like data frames and linear models) as leaves, usingvctrs::obj_is_list()
. To recurse into all objects built on lists useis.list()
.- name_spec
If both inner and outer names are present, control how they are combined. Should be a glue specification that uses variables
inner
andouter
.- name_repair
One of
"minimal"
,"unique"
,"universal"
, or"check_unique"
. Seevctrs::vec_as_names()
for the meaning of these options.
Examples
library(magrittr)
nested_list <- list(1:3, list("foo", list("bar"))) %T>% str()
#> List of 2
#> $ : int [1:3] 1 2 3
#> $ :List of 2
#> ..$ : chr "foo"
#> ..$ :List of 1
#> .. ..$ : chr "bar"
lm_obj <- lm(mpg ~ hp, mtcars)
# unlike `unlist()` which also removes the last list tier from regular lists and many list-based
# objects...
unlist("foobar")
#> [1] "foobar"
unlist(nested_list) |> str()
#> chr [1:5] "1" "2" "3" "foo" "bar"
unlist(lm_obj) |> str()
#> List of 238
#> $ coefficients.(Intercept) : num 30.1
#> $ coefficients.hp : num -0.0682
#> $ residuals.Mazda RX4 : num -1.59
#> $ residuals.Mazda RX4 Wag : num -1.59
#> $ residuals.Datsun 710 : num -0.954
#> $ residuals.Hornet 4 Drive : num -1.19
#> $ residuals.Hornet Sportabout : num 0.541
#> $ residuals.Valiant : num -4.83
#> $ residuals.Duster 360 : num 0.917
#> $ residuals.Merc 240D : num -1.47
#> $ residuals.Merc 230 : num -0.817
#> $ residuals.Merc 280 : num -2.51
#> $ residuals.Merc 280C : num -3.91
#> $ residuals.Merc 450SE : num -1.42
#> $ residuals.Merc 450SL : num -0.518
#> $ residuals.Merc 450SLC : num -2.62
#> $ residuals.Cadillac Fleetwood : num -5.71
#> $ residuals.Lincoln Continental : num -5.03
#> $ residuals.Chrysler Imperial : num 0.294
#> $ residuals.Fiat 128 : num 6.8
#> $ residuals.Honda Civic : num 3.85
#> $ residuals.Toyota Corolla : num 8.24
#> $ residuals.Toyota Corona : num -1.98
#> $ residuals.Dodge Challenger : num -4.36
#> $ residuals.AMC Javelin : num -4.66
#> $ residuals.Camaro Z28 : num -0.0829
#> $ residuals.Pontiac Firebird : num 1.04
#> $ residuals.Fiat X1-9 : num 1.7
#> $ residuals.Porsche 914-2 : num 2.11
#> $ residuals.Lotus Europa : num 8.01
#> $ residuals.Ford Pantera L : num 3.71
#> $ residuals.Ferrari Dino : num 1.54
#> $ residuals.Maserati Bora : num 7.76
#> $ residuals.Volvo 142E : num -1.26
#> $ effects.(Intercept) : num -114
#> $ effects.hp : num -26
#> $ effects3 : num -0.556
#> $ effects4 : num -0.852
#> $ effects5 : num 0.67
#> $ effects6 : num -4.48
#> $ effects7 : num 0.816
#> $ effects8 : num -0.97
#> $ effects9 : num -0.426
#> $ effects10 : num -2.21
#> $ effects11 : num -3.61
#> $ effects12 : num -1.31
#> $ effects13 : num -0.406
#> $ effects14 : num -2.51
#> $ effects15 : num -5.68
#> $ effects16 : num -5.03
#> $ effects17 : num 0.242
#> $ effects18 : num 7.29
#> $ effects19 : num 4.38
#> $ effects20 : num 8.73
#> $ effects21 : num -1.6
#> $ effects22 : num -4.15
#> $ effects23 : num -4.45
#> $ effects24 : num -0.184
#> $ effects25 : num 1.17
#> $ effects26 : num 2.19
#> $ effects27 : num 2.51
#> $ effects28 : num 8.34
#> $ effects29 : num 3.55
#> $ effects30 : num 1.67
#> $ effects31 : num 7.36
#> $ effects32 : num -0.917
#> $ rank : int 2
#> $ fitted.values.Mazda RX4 : num 22.6
#> $ fitted.values.Mazda RX4 Wag : num 22.6
#> $ fitted.values.Datsun 710 : num 23.8
#> $ fitted.values.Hornet 4 Drive : num 22.6
#> $ fitted.values.Hornet Sportabout : num 18.2
#> $ fitted.values.Valiant : num 22.9
#> $ fitted.values.Duster 360 : num 13.4
#> $ fitted.values.Merc 240D : num 25.9
#> $ fitted.values.Merc 230 : num 23.6
#> $ fitted.values.Merc 280 : num 21.7
#> $ fitted.values.Merc 280C : num 21.7
#> $ fitted.values.Merc 450SE : num 17.8
#> $ fitted.values.Merc 450SL : num 17.8
#> $ fitted.values.Merc 450SLC : num 17.8
#> $ fitted.values.Cadillac Fleetwood : num 16.1
#> $ fitted.values.Lincoln Continental: num 15.4
#> $ fitted.values.Chrysler Imperial : num 14.4
#> $ fitted.values.Fiat 128 : num 25.6
#> $ fitted.values.Honda Civic : num 26.6
#> $ fitted.values.Toyota Corolla : num 25.7
#> $ fitted.values.Toyota Corona : num 23.5
#> $ fitted.values.Dodge Challenger : num 19.9
#> $ fitted.values.AMC Javelin : num 19.9
#> $ fitted.values.Camaro Z28 : num 13.4
#> $ fitted.values.Pontiac Firebird : num 18.2
#> $ fitted.values.Fiat X1-9 : num 25.6
#> $ fitted.values.Porsche 914-2 : num 23.9
#> $ fitted.values.Lotus Europa : num 22.4
#> $ fitted.values.Ford Pantera L : num 12.1
#> $ fitted.values.Ferrari Dino : num 18.2
#> $ fitted.values.Maserati Bora : num 7.24
#> $ fitted.values.Volvo 142E : num 22.7
#> [list output truncated]
# ...this function is able to return consistent results, i.e. an unnested list
pal::as_flat_list("foobar", is_node = is.list) |> str()
#> List of 1
#> $ : chr "foobar"
pal::as_flat_list(nested_list, is_node = is.list) |> str()
#> List of 3
#> $ : int [1:3] 1 2 3
#> $ : chr "foo"
#> $ : chr "bar"
pal::as_flat_list(lm_obj, is_node = is.list) |> str()
#> List of 16
#> $ coefficients : Named num [1:2] 30.0989 -0.0682
#> ..- attr(*, "names")= chr [1:2] "(Intercept)" "hp"
#> $ residuals : Named num [1:32] -1.594 -1.594 -0.954 -1.194 0.541 ...
#> ..- attr(*, "names")= chr [1:32] "Mazda RX4" "Mazda RX4 Wag" "Datsun 710" "Hornet 4 Drive" ...
#> $ effects : Named num [1:32] -113.65 -26.046 -0.556 -0.852 0.67 ...
#> ..- attr(*, "names")= chr [1:32] "(Intercept)" "hp" "" "" ...
#> $ rank : int 2
#> $ fitted.values: Named num [1:32] 22.6 22.6 23.8 22.6 18.2 ...
#> ..- attr(*, "names")= chr [1:32] "Mazda RX4" "Mazda RX4 Wag" "Datsun 710" "Hornet 4 Drive" ...
#> $ assign : int [1:2] 0 1
#> $ qr.qr : num [1:32, 1:2] -5.657 0.177 0.177 0.177 0.177 ...
#> ..- attr(*, "dimnames")=List of 2
#> .. ..$ : chr [1:32] "Mazda RX4" "Mazda RX4 Wag" "Datsun 710" "Hornet 4 Drive" ...
#> .. ..$ : chr [1:2] "(Intercept)" "hp"
#> ..- attr(*, "assign")= int [1:2] 0 1
#> $ qr.qraux : num [1:2] 1.18 1.08
#> $ qr.pivot : int [1:2] 1 2
#> $ qr.tol : num 1e-07
#> $ qr.rank : int 2
#> $ df.residual : int 30
#> $ call : language lm(formula = mpg ~ hp, data = mtcars)
#> $ terms :Classes 'terms', 'formula' language mpg ~ hp
#> .. ..- attr(*, "variables")= language list(mpg, hp)
#> .. ..- attr(*, "factors")= int [1:2, 1] 0 1
#> .. .. ..- attr(*, "dimnames")=List of 2
#> .. .. .. ..$ : chr [1:2] "mpg" "hp"
#> .. .. .. ..$ : chr "hp"
#> .. ..- attr(*, "term.labels")= chr "hp"
#> .. ..- attr(*, "order")= int 1
#> .. ..- attr(*, "intercept")= int 1
#> .. ..- attr(*, "response")= int 1
#> .. ..- attr(*, ".Environment")=<environment: 0x55a5f2b65180>
#> .. ..- attr(*, "predvars")= language list(mpg, hp)
#> .. ..- attr(*, "dataClasses")= Named chr [1:2] "numeric" "numeric"
#> .. .. ..- attr(*, "names")= chr [1:2] "mpg" "hp"
#> $ model.mpg : num [1:32] 21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
#> $ model.hp : num [1:32] 110 110 93 110 175 105 245 62 95 123 ...
#> - attr(*, "class")= chr "lm"
nested_list <- list(list(factor("a"), factor("b")), factor("c")) %T>% str()
#> List of 2
#> $ :List of 2
#> ..$ : Factor w/ 1 level "a": 1
#> ..$ : Factor w/ 1 level "b": 1
#> $ : Factor w/ 1 level "c": 1
# unlike `unlist()` which combines factors...
unlist(nested_list) |> str()
#> Factor w/ 3 levels "a","b","c": 1 2 3
# ...this function does not modify the list elements
pal::as_flat_list(nested_list) |> str()
#> List of 3
#> $ : Factor w/ 1 level "a": 1
#> $ : Factor w/ 1 level "b": 1
#> $ : Factor w/ 1 level "c": 1
nested_list <-
list(c(list(1L), list(tibble::tibble(a = list(1.1, "2")))),
list(tibble::as_tibble(mtcars[1:2, ]))) %T>%
str()
#> List of 2
#> $ :List of 2
#> ..$ : int 1
#> ..$ : tibble [2 × 1] (S3: tbl_df/tbl/data.frame)
#> .. ..$ a:List of 2
#> .. .. ..$ : num 1.1
#> .. .. ..$ : chr "2"
#> $ :List of 1
#> ..$ : tibble [2 × 11] (S3: tbl_df/tbl/data.frame)
#> .. ..$ mpg : num [1:2] 21 21
#> .. ..$ cyl : num [1:2] 6 6
#> .. ..$ disp: num [1:2] 160 160
#> .. ..$ hp : num [1:2] 110 110
#> .. ..$ drat: num [1:2] 3.9 3.9
#> .. ..$ wt : num [1:2] 2.62 2.88
#> .. ..$ qsec: num [1:2] 16.5 17
#> .. ..$ vs : num [1:2] 0 0
#> .. ..$ am : num [1:2] 1 1
#> .. ..$ gear: num [1:2] 4 4
#> .. ..$ carb: num [1:2] 4 4
nested_list_2 <- list(1:3, xfun::strict_list(list(list("buried deep")))) %T>% str()
#> List of 2
#> $ : int [1:3] 1 2 3
#> $ :List of 1
#> ..$ :List of 1
#> .. ..$ :List of 1
#> .. .. ..$ : chr "buried deep"
#> ..- attr(*, "class")= chr "xfun_strict_list"
# by default, classed lists like data frames, tibbles or `xfun_strict_list`s are retained, i.e.
# not flattened...
pal::as_flat_list(nested_list) |> str()
#> List of 3
#> $ : int 1
#> $ : tibble [2 × 1] (S3: tbl_df/tbl/data.frame)
#> ..$ a:List of 2
#> .. ..$ : num 1.1
#> .. ..$ : chr "2"
#> $ : tibble [2 × 11] (S3: tbl_df/tbl/data.frame)
#> ..$ mpg : num [1:2] 21 21
#> ..$ cyl : num [1:2] 6 6
#> ..$ disp: num [1:2] 160 160
#> ..$ hp : num [1:2] 110 110
#> ..$ drat: num [1:2] 3.9 3.9
#> ..$ wt : num [1:2] 2.62 2.88
#> ..$ qsec: num [1:2] 16.5 17
#> ..$ vs : num [1:2] 0 0
#> ..$ am : num [1:2] 1 1
#> ..$ gear: num [1:2] 4 4
#> ..$ carb: num [1:2] 4 4
pal::as_flat_list(nested_list_2) |> str()
#> List of 2
#> $ : int [1:3] 1 2 3
#> $ :List of 1
#> ..$ :List of 1
#> .. ..$ :List of 1
#> .. .. ..$ : chr "buried deep"
#> ..- attr(*, "class")= chr "xfun_strict_list"
# ...but you can drop them and thereby flatten custom objects if needed
pal::as_flat_list(nested_list, is_node = is.list) |> str()
#> List of 14
#> $ : int 1
#> $ a.1 : num 1.1
#> $ a.2 : chr "2"
#> $ mpg : num [1:2] 21 21
#> $ cyl : num [1:2] 6 6
#> $ disp: num [1:2] 160 160
#> $ hp : num [1:2] 110 110
#> $ drat: num [1:2] 3.9 3.9
#> $ wt : num [1:2] 2.62 2.88
#> $ qsec: num [1:2] 16.5 17
#> $ vs : num [1:2] 0 0
#> $ am : num [1:2] 1 1
#> $ gear: num [1:2] 4 4
#> $ carb: num [1:2] 4 4
pal::as_flat_list(nested_list_2, is_node = is.list) |> str()
#> List of 2
#> $ : int [1:3] 1 2 3
#> $ : chr "buried deep"