使用 unlist 递归地简化列表
simplify lists recursively using unlist
考虑这样一个案例:
xml_list <- list(
a = "7",
b = list("8"),
c = list(
c.a = "7",
c.b = list("8"),
c.c = list("9", "10"),
c.d = c("11", "12", "13")),
d = c("a", "b", "c"))
我正在寻找的是一种如何递归地简化此构造的方法,以便在长度为 1 的任何 list
上调用 unlist
。上述示例的预期结果如下所示:
list(
a = "7",
b = "8",
c = list(
c.a = "7",
c.b = "8",
c.c = list("9", "10"),
c.d = c("11", "12", "13")),
d = c("a", "b", "c"))
我已经涉足 rapply
,但它明确地对 list
成员进行操作,这些成员是 NOT 列表本身,因此写了以下内容:
library(magrittr)
clean_up_list <- function(xml_list){
xml_list %>%
lapply(
function(x){
if(is.list(x)){
if(length(x) == 1){
x %<>%
unlist()
} else {
x %<>%
clean_up_list()
}
}
return(x)
})
}
但是,我什至无法测试,因为 Error: C stack usage 7969588 is too close to the limit
(至少在我最终想要处理的列表上)。
深入挖掘(仔细考虑@Roland 的回复后),我想出了一个解决方案,利用 purrr
-goodness,反向迭代列表深度和 NEARLY做我想做的事:
clean_up_list <- function(xml_list)
{
list_depth <- xml_list %>%
purrr::vec_depth()
for(dl in rev(sequence(list_depth)))
{
xml_list %<>%
purrr::modify_depth(
.depth = dl,
.ragged = TRUE,
.f = function(x)
{
if(is.list(x) && length(x) == 1 && length(x[[1]]) == 1)
{
unlist(x, use.names = FALSE)
} else {
x
}
})
}
return(xml_list)
}
即使对于我正在处理的 BUT 曾经是矢量的元素(如 c.d
和 d
在示例中)现在被转换为 lists
,这违背了目的……还有什么进一步的见解吗?
我不明白 magrittr 的东西,但是创建递归函数很容易:
foo <- function(L) lapply(L, function(x) {
if (is.list(x) && length(x) > 1) return(foo(x))
if (is.list(x) && length(x) == 1) x[[1]] else x
})
foo(test_list)
#$`a`
# [1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z"
#
#$b
#[1] "a"
#
#$c
#$c$`c.1`
#[1] "b"
#
#$c$c.2
# [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
#
#$c$c.3
#$c$c.3[[1]]
#[1] "c"
#
#$c$c.3[[2]]
#[1] "d"
如果这会引发有关 C 堆栈使用的错误,那么您的列表是深度嵌套的。那时你不能使用递归,这会使它成为一个具有挑战性的问题。如果可能的话,我会修改这个列表的创建。或者您可以尝试 increase the C stack size.
借助 ticket 针对 purrr
的 github
存储库,我解决了这个问题:使用当前开发者版本的 purrr
(可通过 [=13 安装=]),问题中的 purrr
-dpendent 代码按预期工作,不再是 "listify" 向量。因此,该代码应该作为问题的答案,并在 2018/19 新年之后与 CRAN
-borne 包一起完全发挥作用。
考虑这样一个案例:
xml_list <- list(
a = "7",
b = list("8"),
c = list(
c.a = "7",
c.b = list("8"),
c.c = list("9", "10"),
c.d = c("11", "12", "13")),
d = c("a", "b", "c"))
我正在寻找的是一种如何递归地简化此构造的方法,以便在长度为 1 的任何 list
上调用 unlist
。上述示例的预期结果如下所示:
list(
a = "7",
b = "8",
c = list(
c.a = "7",
c.b = "8",
c.c = list("9", "10"),
c.d = c("11", "12", "13")),
d = c("a", "b", "c"))
我已经涉足 rapply
,但它明确地对 list
成员进行操作,这些成员是 NOT 列表本身,因此写了以下内容:
library(magrittr)
clean_up_list <- function(xml_list){
xml_list %>%
lapply(
function(x){
if(is.list(x)){
if(length(x) == 1){
x %<>%
unlist()
} else {
x %<>%
clean_up_list()
}
}
return(x)
})
}
但是,我什至无法测试,因为 Error: C stack usage 7969588 is too close to the limit
(至少在我最终想要处理的列表上)。
深入挖掘(仔细考虑@Roland 的回复后),我想出了一个解决方案,利用 purrr
-goodness,反向迭代列表深度和 NEARLY做我想做的事:
clean_up_list <- function(xml_list)
{
list_depth <- xml_list %>%
purrr::vec_depth()
for(dl in rev(sequence(list_depth)))
{
xml_list %<>%
purrr::modify_depth(
.depth = dl,
.ragged = TRUE,
.f = function(x)
{
if(is.list(x) && length(x) == 1 && length(x[[1]]) == 1)
{
unlist(x, use.names = FALSE)
} else {
x
}
})
}
return(xml_list)
}
即使对于我正在处理的 BUT 曾经是矢量的元素(如 c.d
和 d
在示例中)现在被转换为 lists
,这违背了目的……还有什么进一步的见解吗?
我不明白 magrittr 的东西,但是创建递归函数很容易:
foo <- function(L) lapply(L, function(x) {
if (is.list(x) && length(x) > 1) return(foo(x))
if (is.list(x) && length(x) == 1) x[[1]] else x
})
foo(test_list)
#$`a`
# [1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z"
#
#$b
#[1] "a"
#
#$c
#$c$`c.1`
#[1] "b"
#
#$c$c.2
# [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
#
#$c$c.3
#$c$c.3[[1]]
#[1] "c"
#
#$c$c.3[[2]]
#[1] "d"
如果这会引发有关 C 堆栈使用的错误,那么您的列表是深度嵌套的。那时你不能使用递归,这会使它成为一个具有挑战性的问题。如果可能的话,我会修改这个列表的创建。或者您可以尝试 increase the C stack size.
借助 ticket 针对 purrr
的 github
存储库,我解决了这个问题:使用当前开发者版本的 purrr
(可通过 [=13 安装=]),问题中的 purrr
-dpendent 代码按预期工作,不再是 "listify" 向量。因此,该代码应该作为问题的答案,并在 2018/19 新年之后与 CRAN
-borne 包一起完全发挥作用。