Build nested named list from list in R

Viewed 126

I’m trying to prepare a data structure which I will need at later point in my script. Let’s say I’m in a graph domain and my nodes (sometimes only 1, but mostly 2 or even 3) have names (e.g., "left", "right", "center"). The names are stored in a variable:

origin_target_list <- c("left", "center", "right", "")

Given the list with the three items from above, I’m trying to create this data structure dynamically:

origin_target_combinations <- list(
  left = list(    # origin is left
    right = c(),  # target is right
    center = c(), # target is center
    unknown = c() # target is somewhere else / unknown
  ),
  right = list(
    left = c(),
    center = c(),
    unknown = c()
  ),
  center = list(
    left = c(),
    right = c(),
    unknown = c()
  ),
  unknown = list( # origin is unknown
    left = c(),   # target is left
    center = c(), # target is center
    right = c()   # target is right
  )
)

EDIT: The constrain is origin and target cannot be the same. Thus, every element in the origin_target_list plus "unknown" should be combined.

Similar, a list with one element: origin_target_list <- c("center") would only create the unknown case:

origin_target_combinations <- list(
  unknown = list(
    left = c(),
    center = c(),
    right = c()
  )
)

I know this is a complex problem, I working on a solution myself right now... I'll keep this post updated with my attempts...

3 Answers

You can do as follows

# assign function to create the list
create_list <- function(args, val = c())
  setNames(
    lapply(args, function(x)
      setNames(rep(list(val), length(args) - 1L), setdiff(args, x))), 
    args)

# use the function
str(create_list(c("left", "center", "right", "unknown")))
#R> List of 4
#R>  $ left  :List of 3
#R>   ..$ center: NULL
#R>   ..$ right : NULL
#R>   ..$ unknown: NULL
#R>  $ center:List of 3
#R>   ..$ left  : NULL
#R>   ..$ right : NULL
#R>   ..$ unknown: NULL
#R>  $ right :List of 3
#R>   ..$ left  : NULL
#R>   ..$ center: NULL
#R>   ..$ unknown: NULL
#R>  $ unknown:List of 3
#R>   ..$ left  : NULL
#R>   ..$ center: NULL
#R>   ..$ right : NULL

# you can change the type and the element names like so
str(create_list(c("cat", "dog", "bird"), numeric()))
#R> List of 3
#R>  $ cat :List of 2
#R>   ..$ dog : num(0) 
#R>   ..$ bird: num(0) 
#R>  $ dog :List of 2
#R>   ..$ cat : num(0) 
#R>   ..$ bird: num(0) 
#R>  $ bird:List of 2
#R>   ..$ cat: num(0) 
#R>   ..$ dog: num(0) 

This function should do what you try to do. But you have to fill the list elements with something more than just an empty vector as they are removed again if they are empty. So, I fill them with a vector containing a single NA:

make_dynlist = function(nodes){
  l = list("unknown"=list("right"=c(NA),
                          "left"=c(NA),
                          "center"=c(NA)))
  if(length(nodes)>1){
    for(n in c(nodes)){
      l[[n]]=list()
      for(d in nodes){
        if(n!=d){
          l[[n]][[d]]=c(NA)
        }
      }
    }
  }
  
  return(l)
}

The output would be:

> make_dynlist(c("left", "center", "right"))
$unknown
$unknown$right
[1] NA

$unknown$left
[1] NA

$unknown$center
[1] NA


$left
$left$center
[1] NA

$left$right
[1] NA


$center
$center$left
[1] NA

$center$right
[1] NA


$right
$right$left
[1] NA

$right$center
[1] NA


> make_dynlist(c("center"))
$unknown
$unknown$right
[1] NA

$unknown$left
[1] NA

$unknown$center
[1] NA

Here's a simple implementation using lapply:

make_list <- function(branches)
{
  setNames(lapply(branches, function(origin)
  {
    blank <- list(left = c(), right = c(), center = c(), unknown = c())
    blank[!(names(blank) %in% origin)]
  }), branches)
}

Yielding

make_list(c("left", "right", "center", "unknown"))
#> $left
#> $left$right
#> NULL
#> 
#> $left$center
#> NULL
#> 
#> $left$unknown
#> NULL
#> 
#> 
#> $right
#> $right$left
#> NULL
#> 
#> $right$center
#> NULL
#> 
#> $right$unknown
#> NULL
#> 
#> 
#> $center
#> $center$left
#> NULL
#> 
#> $center$right
#> NULL
#> 
#> $center$unknown
#> NULL
#> 
#> 
#> $unknown
#> $unknown$left
#> NULL
#> 
#> $unknown$right
#> NULL
#> 
#> $unknown$center
#> NULL

Created on 2020-08-17 by the reprex package (v0.3.0)

Related