将图层添加到以下名称的列表中

Add a layer to a list following names

提问人:Levon Ipdjian 提问时间:11/9/2023 更新时间:11/9/2023 访问量:63

问:

我有这个图层列表:

have <- list("a" = 1, "b.a" = 2, "b.b" = 3)

我需要这个 2 层列表:

need <- list("a" = 1, "b" = list("a" = 2, "b" = 3))

如果解决方案可以是递归的,那就更好了。目标是结构化数据,例如,它可以解析为某个 Json 文件。

谢谢!

r 咕噜咕噜

评论


答:

2赞 Allan Cameron 11/9/2023 #1

这个递归函数应该可以解决问题:

f <- function(obj) {
  nms <- lapply(strsplit(names(obj), '\\.'), function(x){
    if(length(x) == 1) x else c(x[1], paste(x[-1], collapse = '.'))
  })
  first_nms <- sapply(nms, `[`, 1)
  last_nms <- sapply(nms, `[`, -1)
  un_nms <- unique(first_nms)
  setNames(lapply(un_nms, function(x) {
    o <- unname(obj[first_nms %in% x])
    if(length(o) == 1) unlist(o) else
      f(setNames(o, last_nms[first_nms %in% x]))
    }), un_nms)
}

测试,我们有:

have <- list("a" = 1, "b.a" = 2, "b.b" = 3)

f(have)
#> $a
#> [1] 1
#> 
#> $b
#> $b$a
#> [1] 2
#> 
#> $b$b
#> [1] 3

我们可以看到它适用于深度嵌套列表:

f(list(a = 1, b.a = 2, b.b = 3, c.a.a = 4, c.a.b = 5, c.b.a = 6, c.b.b = 7))
#> $a
#> [1] 1
#> 
#> $b
#> $b$a
#> [1] 2
#> 
#> $b$b
#> [1] 3
#> 
#> 
#> $c
#> $c$a
#> $c$a$a
#> [1] 4
#> 
#> $c$a$b
#> [1] 5
#> 
#> 
#> $c$b
#> $c$b$a
#> [1] 6
#> 
#> $c$b$b
#> [1] 7

评论

0赞 Levon Ipdjian 11/9/2023
我喜欢这个解决方案。我想没有比这更简单的了。谢谢!
2赞 Joris C. 11/9/2023 #2

这是另一种使用 (package ) 的方法。首先,我们用 将列表融化到 data.frame 中,然后将名称拆分为单独的列,最后将 data.frame 转换回嵌套列表:rrapply()rrapplyrrapply(how = "melt")rrapply(how = "unmelt")

library(rrapply)
library(tidyr)
library(data.table)

have <- list("a" = 1, "b.a" = 2, "b.b" = 3)

## tidyverse 
need_tidy <- rrapply(have, how = "melt") |>
  separate_wider_delim(L1, delim = ".", names_sep = "_", too_few = "align_start") |>
  rrapply(how = "unmelt")

str(need_tidy)
#> List of 2
#>  $ a: num 1
#>  $ b:List of 2
#>   ..$ a: num 2
#>   ..$ b: num 3

## data.table
need_dt <- as.data.table(rrapply(have, how = "melt"))
need_dt <- cbind(need_dt[, tstrsplit(L1, split = "\\.")], need_dt[, "value"])
need_dt <- rrapply(need_dt, how = "unmelt")

str(need_dt)
#> List of 2
#>  $ a: num 1
#>  $ b:List of 2
#>   ..$ a: num 2
#>   ..$ b: num 3

相同的代码也可用于构造更深的嵌套列表:

## deeply nested list
have <- list("a" = 1, "b.a.x.y.z" = 2, "b.b" = 3)

need_tidy <- rrapply(have, how = "melt") |>
  separate_wider_delim(L1, delim = ".", names_sep = "_", too_few = "align_start") |>
  rrapply(how = "unmelt")

str(need_tidy)
#> List of 2
#>  $ a: num 1
#>  $ b:List of 2
#>   ..$ a:List of 1
#>   .. ..$ x:List of 1
#>   .. .. ..$ y:List of 1
#>   .. .. .. ..$ z: num 2
#>   ..$ b: num 3

评论

0赞 ThomasIsCoding 11/9/2023
这很酷!现在我知道有包可以做到,干杯!