如何绑定具有相同结构的两个列表?

rma*_*gno 6 r list purrr

介绍

我有两个嵌套列表,其结构与我想要组合的结构相同(在c()某种意义上).

对于这种关系,我可能已经存在一个关于图论或计算机科学中相同结构的概念,但我不知道.

所以这是我试图通过相同的结构澄清我的意思:

  • 某个级别的列表元素要么全部命名,要么没有命名;
  • 当我们命名元素时,在该级别上永远不会有重复的名称;
  • 当节点本身是命名元素时,两个列表的父子节点关系是相同的.

所以我想知道是否已经有一个解决这个问题的方法,我认为这可能是相当普遍和常见的...(?)任何解决方案涉及:

  • 使用基础rapply;
  • 具有某些purrr功能组合的Tidyverse解决方案;
  • rlist包中的功能

会很好!

foo并且bar是具有相同结构的两个示例列表.

wonderful是由组合foobar(手动完成)产生的所需列表.

我希望它足够清楚!

# Input lists: foo and bar
foo <- list(a = list(a1 = 1:3, a2 = rep('a', 3)), b = list(b1 = list(b11 = c(4,5,6), b12 = rep('b', 3)), b2 = list(b21 = list(b31 = c(0, 1, 2)))), c = list(list(c21 = 1:3), list(c21 = 4:6), list(c21 = 7:9)))
bar <- list(a = list(a1 = 1:3, a2 = rep('z', 3)), b = list(b1 = list(b11 = c(-1,2,5), b12 = rep('b', 3)), b2 = list(b21 = list(b31 = -c(1,2,3)))), c = list(list(c21 = 3:1), list(c21 = 5:3)))

# wonderful: desired list (result from combining foo and bar)
wonderful <- list(
  a = list(
    a1 = c(foo$a$a1, bar$a$a1), 
    a2 = c(foo$a$a2, bar$a$a2)
    ),
  b = list(
    b1 = list(
      b11 = c(foo$b$b1$b11, bar$b$b1$b11),
      b12 = c(foo$b$b1$b12, bar$b$b1$b12)
      ),
    b2 = list(
      b21 = list(
        b31 = c(foo$b$b2$b21$b31, bar$b$b2$b21$b31)
        )
      )
    ),
  c = c(foo$c, bar$c)
)

str(foo)
#> List of 3
#>  $ a:List of 2
#>   ..$ a1: int [1:3] 1 2 3
#>   ..$ a2: chr [1:3] "a" "a" "a"
#>  $ b:List of 2
#>   ..$ b1:List of 2
#>   .. ..$ b11: num [1:3] 4 5 6
#>   .. ..$ b12: chr [1:3] "b" "b" "b"
#>   ..$ b2:List of 1
#>   .. ..$ b21:List of 1
#>   .. .. ..$ b31: num [1:3] 0 1 2
#>  $ c:List of 3
#>   ..$ :List of 1
#>   .. ..$ c21: int [1:3] 1 2 3
#>   ..$ :List of 1
#>   .. ..$ c21: int [1:3] 4 5 6
#>   ..$ :List of 1
#>   .. ..$ c21: int [1:3] 7 8 9

str(bar)
#> List of 3
#>  $ a:List of 2
#>   ..$ a1: int [1:3] 1 2 3
#>   ..$ a2: chr [1:3] "z" "z" "z"
#>  $ b:List of 2
#>   ..$ b1:List of 2
#>   .. ..$ b11: num [1:3] -1 2 5
#>   .. ..$ b12: chr [1:3] "b" "b" "b"
#>   ..$ b2:List of 1
#>   .. ..$ b21:List of 1
#>   .. .. ..$ b31: num [1:3] -1 -2 -3
#>  $ c:List of 2
#>   ..$ :List of 1
#>   .. ..$ c21: int [1:3] 3 2 1
#>   ..$ :List of 1
#>   .. ..$ c21: int [1:3] 5 4 3

str(wonderful)
#> List of 3
#>  $ a:List of 2
#>   ..$ a1: int [1:6] 1 2 3 1 2 3
#>   ..$ a2: chr [1:6] "a" "a" "a" "z" ...
#>  $ b:List of 2
#>   ..$ b1:List of 2
#>   .. ..$ b11: num [1:6] 4 5 6 -1 2 5
#>   .. ..$ b12: chr [1:6] "b" "b" "b" "b" ...
#>   ..$ b2:List of 1
#>   .. ..$ b21:List of 1
#>   .. .. ..$ b31: num [1:6] 0 1 2 -1 -2 -3
#>  $ c:List of 5
#>   ..$ :List of 1
#>   .. ..$ c21: int [1:3] 1 2 3
#>   ..$ :List of 1
#>   .. ..$ c21: int [1:3] 4 5 6
#>   ..$ :List of 1
#>   .. ..$ c21: int [1:3] 7 8 9
#>   ..$ :List of 1
#>   .. ..$ c21: int [1:3] 3 2 1
#>   ..$ :List of 1
#>   .. ..$ c21: int [1:3] 5 4 3
Run Code Online (Sandbox Code Playgroud)

zac*_*ack 3

尝试一下:

library(purrr)

rec_map <- function(fizz, buzz) {
  if(is.atomic(fizz) | is.null(names(fizz))){
    c(fizz, buzz)
  } else {
    imap(fizz,
         ~rec_map(fizz[[.y]], buzz[[.y]]))
  }
}

temp <- rec_map(foo, bar)

all.equal(temp, wonderful)
#> [1] TRUE
Run Code Online (Sandbox Code Playgroud)

我绝不是一名计算机科学家,所以对这个解决方案持保留态度。当一级没有名称,但下一级有名称(例如,foo$c)时,我不确定所需的行为。c()因此,如果我们遇到没有名称的关卡,我只是将结果合并起来 ( )。

编辑以获取多个列表:

prec_map <- function(...){
  dots <- list(...)
  first_el = dots[[1]]
  if(is.atomic(first_el) | is.null(names(first_el))){
    do.call(c, dots)
  } else {
    imap(first_el,
         function(el, nme){
           one_level_down <- map(dots, nme)
           do.call(prec_map, one_level_down)
         })
  }
}

temp <- prec_map(foo, bar)

all.equal(temp, wonderful)
[1] TRUE
Run Code Online (Sandbox Code Playgroud)

我还没有彻底测试过它,但简单的测试看起来它已经完成了工作。

  • `prec_map` 可以表示为 `function(.l) reduce(.l, rec_map)` 或 `partial(reduce, .f = rec_map)` 或 `. %&gt;% reduce(rec_map)`,用作`prec_map(list(foo, bar))` (2认同)