如何使用rrapply获取所有列表元素(含空列表)的索引位置
嵌套列表元素位置提取问题
我有一个嵌套列表结构,叶子节点为空列表,父列表仅包含子列表,需要提取所有列表元素的索引位置用于遍历。使用rrapply处理含元素的列表时有效,但无法获取无嵌套非列表子元素的空列表的位置。以下是示例代码,展示rrapply的输出缺失情况,以及我自行编写的解决方案,希望能用rrapply实现需求,并理解之前无法实现的原因。
# stackoverflow_list-element-position-vector.R library(rrapply) library(purrr) # 示例列表 lst <- list( list(), list( list(), list(), list() ), list(a=1:3 , list() ) ) # 仅获取到含名称"a"的子列表,遗漏所有空列表: rrapply(lst, f = \(x, .xpos) .xpos, how="melt") |> pluck("value") |> str() #> List of 1 #> $ : int [1:2] 3 1 # 社区方案:遗漏了中间节点的位置,比如位置2和3的列表 rrapply(lst , classes = "list" , condition = \(x) length(x) == 0 , f = \(x) NA , how="recurse") |> rrapply(f = \(x, .xpos) .xpos , how="melt") |> pluck("value") |> str() #> List of 6 #> $ : int 1 #> $ : int [1:2] 2 1 #> $ : int [1:2] 2 2 #> $ : int [1:2] 2 3 #> $ : int [1:2] 3 1 #> $ : int [1:2] 3 2 # 自定义函数:输入列表,返回所有列表元素的位置向量组成的扁平列表 listEltPos <- function(llst) { work_lst <- list(list(coord_vec = integer(0), comp = llst)) output_lst <- list() while (TRUE) { if (length(work_lst) == 0) break # 取出work_lst的首个元素 e <- pluck(work_lst, 1) work_lst <- work_lst[-1] # 若元素不是列表,跳过 if (class(e$comp) != "list") next # 若列表为空,跳过 if (length(e$comp) == 0) next # 处理子元素位置 subElementPositions <- seq(length(e$comp)) for (i in subElementPositions) { # 拼接坐标向量 newPosVec <- c(e$coord_vec, i) sub_e <- pluck(e$comp, i) # 添加到输出列表 output_lst <- append(output_lst, list(newPosVec)) # 将子元素加入工作队列 work_lst <- append(work_lst, list(list(coord_vec=newPosVec, comp=sub_e))) } rm(i) } output_lst } # 这是期望的结果 listEltPos(lst) |> str() #> List of 8 #> $ : int 1 #> $ : int 2 #> $ : int 3 #> $ : int [1:2] 2 1 #> $ : int [1:2] 2 2 #> $ : int [1:2] 2 3 #> $ : int [1:2] 3 1 #> $ : int [1:2] 3 2
创建于2023-05-03,使用reprex v2.0.2
问题原因分析
首次
rrapply调用缺失结果:
默认情况下,rrapply仅对非列表类型的元素执行函数逻辑,所有列表节点(包括空列表)会被直接跳过,因此仅捕获到了位置c(3,1)的非列表元素(1:3)。二次调用遗漏中间节点:
先标记空列表再提取位置的逻辑,仅处理了空列表和原有非列表元素,没有覆盖非空父列表的子元素位置(如顶层的位置2、3),导致结果缺失。
正确的rrapply实现方案
只需通过exclude_root = TRUE排除顶层列表,让rrapply遍历所有子节点并返回位置向量,即可得到与自定义函数一致的结果:
# 使用rrapply获取所有非顶层节点的位置向量 rrapply( lst, f = \(x, .xpos) .xpos, how = "flatten", exclude_root = TRUE ) |> str() #> List of 8 #> $ : int 1 #> $ : int 2 #> $ : int 3 #> $ : int [1:2] 2 1 #> $ : int [1:2] 2 2 #> $ : int [1:2] 2 3 #> $ : int [1:2] 3 1 #> $ : int [1:2] 3 2
核心参数说明
exclude_root = TRUE:跳过输入的顶层列表,仅处理其所有子节点;f = \(x, .xpos) .xpos:对每个子节点返回其在嵌套结构中的完整位置向量;how = "flatten":将递归处理的结果压平为扁平列表,与自定义函数输出格式匹配。
内容的提问来源于stack exchange,提问作者mpettis
相关产品推荐
相关产品推荐

