从字符串创建嵌套列表结构

5

我有一个字符串,由n个子字符串组成。它可以看起来像这样:

string <- c("A_AA", "A_BB", "A_BB_AAA", "B_AA", "B_BB", "B_CC")

这个字符串中的每个子组件都用“_”分隔开来。在这里,第一层由值“A”和“B”组成,第二层由“AA”、“BB”和“CC”组成,第三层由“AAA”组成。更深层次的嵌套也是可能的,解决方案应该扩展到这些情况。嵌套不一定平衡,例如,“A”只有两个子项,而“B”有三个子项,但它还有一个“B”没有的孙子。

本质上,我想要在某种R对象中重新创建此字符串的嵌套结构,最好是一个列表。因此,嵌套列表结构看起来像这样:

list("A" = list("AA", "BB" = list("AAA")),
"B" = list("AA", "BB", "CC"))

> $A
  $A[[1]]

  [1] "AA"
  $A$BB
  $A$BB[[1]]
  [1] "CCC"

  $B
  $B[[1]]
  [1] "AA"

  $B[[2]]
  [1] "BB"

  $B[[3]]
  [1] "CC"

任何对此的帮助都将不胜感激

3个回答

3
你可以很容易地把它转化成矩阵,不需要太麻烦的步骤...
string <- c("A_AA", "A_BB", "A_BB_AAA", "B_AA", "B_BB", "B_CC")

splitted<-strsplit(string,"_")
cols<-max(lengths(splitted))
mat<-do.call(rbind,lapply(splitted, "length<-", cols))

你可以用 lengths(x) 代替 lapply(x, length),并在 "cols" 后面使用 do.call(rbind, lapply(splitted, "length<-", cols)) - alexis_laz
1
这会导致一种与OP想要的非常不同的结构。除非从矩阵到OP的格式存在微不足道的转换,否则我认为那不是他们想要的(而且我也看不出来)。 - Konrad Rudolph
@KonradRudolph,楼主确实偏好于列表的列表,但似乎也可以接受其他结构。 - Dean MacGregor
感谢您的努力,但我认为矩阵格式对我并没有太大用处。我需要使用生成的对象来确定在A“节点”下有多少个子代/孙代。我不确定这是否可以轻松地通过矩阵实现(很抱歉一开始没有表达清楚)。 - user3393472
1
@user3393472,使用矩阵比使用列表更容易。当您在列表上执行length操作时,它将把元素也是列表的元素视为1,而不会计算列表中所有子元素的数量。然而,使用矩阵,您可以执行subset(mat,mat[,1]=="A"),然后计算任何一列中有多少个na。 - Dean MacGregor
@dean-macgregor,我实现了你的解决方案,对我来说效果很好。对我来说关键点始终是能够查询数据结构,了解特定节点下有多少子代/孙代。通过对列进行子集和一些迭代机制(lapply),我可以完全实现这一点。谢谢。 - user3393472

1

虽然不是最简单的代码,也不是最美丽的代码,但它应该能够完成任务并返回一个列表:

string <- c("A_AA", "A_BB", "A_BB_AAA", "B_AA", "B_BB", "B_CC")

# loop through each element of the string "str_el"
list_els <- lapply(string, function(str_el) {

  # split the string into parts
  els <- strsplit(str_el, "_")[[1]]

  # loop backwards through the elements
  for (i in length(els):1){

    # the last element gives the value
    if (i == length(els)){

      # assign the value to a list and rename the list          
      res <- list(els[[i]])
      names(res) <- els[[i - 1]]

    } else {
      # if its not the last element (value) assign the list res to another list
      # with the name of that element
      if (i != 1) {
        res <- list(res)
        names(res) <- els[[i - 1]]
      }
    }
  }

  return(res)
})

# combine the lists into one list
res_list <- mapply(c, list_els, SIMPLIFY = F)

res_list
# [[1]]
# [[1]]$A
# [1] "AA"
# 
# 
# [[2]]
# [[2]]$A
# [1] "BB"
# 
# 
# [[3]]
# [[3]]$A
# [[3]]$A$BB
# [1] "AAA"
# 
# 
# 
# [[4]]
# [[4]]$B
# [1] "AA"
# 
# 
# [[5]]
# [[5]]$B
# [1] "BB"
# 
# 
# [[6]]
# [[6]]$B
# [1] "CC"

那是否给你想要的东西?


谢谢,它已经很接近了。针对我的问题,需要list_els[[1]]给出包含所有子节点和孙子节点的整个A“节点”。 - user3393472
这似乎是一个真正的烦恼。你能详细说明一下你想要实现什么吗?我会遵循@Dean的答案,使用某种非列表结构。列表在某些情况下确实很有用,但嵌套列表并不那么有趣。 - David
我暂时坚持使用Dean的解决方案。感谢你的努力。 - user3393472

0

我找到了一种方法来做这件事。虽然有点奇怪,但似乎可以工作。

my_relist <- function(x){
y=list()
#This first loop creates the skeleton of the list
for (name in x){
    split=strsplit(name,'_',fixed=TRUE)[[1]]
    char='y'
    l=length(split)
    for (i in 1:(l-1)){
        char=paste(char,'$',split[i],sep="")
    }
char2=paste(char,'= list()',sep="")
#Example of char2: "y$A$BB=list()"
eval(parse(text=char2))
#Evaluates the expression inside char2
}

#The second loop fills the list with the last element
for (name in x){
   split=strsplit(name,'_',fixed=TRUE)[[1]]
   char='y'
   l=length(split)
   for (i in 1:(l-1)){
       char=paste(char,'$',split[i],sep="")
   }
char3=paste(char,'=c(',char,',split[l])')
#Example of char3: "y$A = c(y$A,"BB")"
eval(parse(text=char3))
}
return(y)
}

这是结果:

example <- c("A_AA_AAA", "A_BB", "A_BB_AAA", "B_AA", "B_BB", "B_CC")
my_relist(example)
#$A
#$BB
#1.'AAA'
#[[2]]
#'AA'
#[[3]]
#'BB'
#$B
#1.'AA'
#2.'BB'
#3.'CC'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接