R:从矢量路径生成多级列表?

时间:2019-01-28 18:00:49

标签: r list nested

我正在尝试构建一个奇迹函数,该函数将递归创建/更改列表。类似于以下内容

miracle <- function(lst = NULL, path = c('a', 'a.a', 'a.a.a'), value = 'Something')
{
  if(is.null(lst)) lst <- list()
  <MIRACLE HERE>
  return(lst)
}

应该产生list(a = list(a.a = list(a.a.a = 'Something')))作为返回值(意味着它会在新列表中生成路径),或者如果lst是一个预先存在的列表(包括该路径),则其等效于lst[['a']][['a.a']][['a.a.a']] <- value-但与路径深度无关。

该如何处理?数小时的谷歌搜索和玩data.tree以及类似的游戏不允许选择。

2 个答案:

答案 0 :(得分:1)

这是您要寻找的吗?

miracle <- function(lst = NULL, path = c('a', 'a.a', 'a.a.a'), value = 'Something') {
  if (length(path) == 1) {
    lst[[path[1]]] <- value
    return(lst)
  }
  temp <- list()
  for (i in length(path):2) {
    ptemp = path[i]
    if (i == length(path)) {
      temp[[ptemp]] = value
    } else {
      temp[[ptemp]] = temp
      temp[[1]] <- NULL
    }
  }
  lst[[path[i-1]]] <- temp
  return(lst)
}

答案 1 :(得分:0)

绕了几个弯路(例如,参见herehere),并根据上面的@ pawel-chabros提示,我想到了下面的(有些详尽)功能,如所希望的那样产生:

> # Create a deep list
> example_list <- list_access(list(), path = c('A', 'AA', 'AAA', 'AAAA'),'Something')
> str(example_list)
List of 1
 $ A:List of 1
  ..$ AA:List of 1
  .. ..$ AAA:List of 1
  .. .. ..$ AAAA: chr "Something"
> # Modify the list
> example_list <- list_access(example_list, path = c('A', 'AA', 'AAB'), 'Something else')
> str(example_list)
List of 1
 $ A:List of 1
  ..$ AA:List of 2
  .. ..$ AAA:List of 1
  .. .. ..$ AAAA: chr "Something"
  .. ..$ AAB: chr "Something else"
> # Access an element
> list_access(example_list, path = c('A', 'AA', 'AAA', 'AAAA'))
[1] "Something"
> # Access multiple elements
> list_access(example_list, path = list(c('A', 'AA', 'AAA', 'AAAA'), c('A', 'AA', 'AAB')))
[1] "Something"      "Something else"
> # Delete an element
> example_list <- list_access(lst = example_list, path = c('A', 'AA', 'AAB'), NULL)
> str(example_list)
List of 1
 $ A:List of 1
  ..$ AA:List of 1
  .. ..$ AAA:List of 1
  .. .. ..$ AAAA: chr "Something"
> # Multiple edits
> example_list <- list_access(example_list,
    path = list( c('A', 'AA', 'AAB'), c('A', 'AB'), c('B', 'BA', 'BAA')),
    'Something else (again)', 'Entirely different', 'Weird and beautiful')
> str(example_list)
List of 2
 $ A:List of 2
  ..$ AA:List of 2
  .. ..$ AAA:List of 1
  .. .. ..$ AAAA: chr "Something"
  .. ..$ AAB: chr "Something else (again)"
  ..$ AB: chr "Entirely different"
 $ B:List of 1
  ..$ BA:List of 1
  .. ..$ BAA: chr "Weird and beautiful"

我将阐述并使用它来管理我的一个项目的深层参数列表。 我无法实现的一件事是在生成而不是修改列表时调用list_access(path = c('A', 'AA'), 'Something')(没有明确的lst参数)。

以下是功能:

library(assertive.base)
library(magrittr)
library(purrr)
list_access <- function(lst = list(), path, ...) {
  # Capture parameters ------------------------------------------------------
  value <- list(...) %>%
    unlist(recursive = FALSE)
  retrieve <- missing(...)
  # <Input checking omited>
  # Processing --------------------------------------------------------------
  # Branch: insert or retrieve value
  ## Retrieve
  if(retrieve){
    ### Multiple retrievals
    if(is.list(path)){
      output <- sapply(
        path,
        function(x){
          #### Check for path existence
          preexists <- list_path_preexists(lst, x)
          if(retrieve) assertive.base::assert_all_are_true(preexists)
          tmp_lst <- lst
          for(pi in x){
            tmp_lst %<>%
              magrittr::extract2(pi)
          }
          return(tmp_lst)
        }
      )
    ### Single retrieval
    } else {
      #### Check for path existence
      preexists <- list_path_preexists(lst, path)
      if(retrieve) assertive.base::assert_all_are_true(preexists)
      output <- lst
      for(pi in path){
        output %<>%
          magrittr::extract2(pi)
      }
    }
  ## Insert
  } else {
    output <- lst
    ### Multiple inserts
    if(is.list(path)){
      for(i in seq_along(path)){
        modifier <- list()
        tmp_path <- path[[i]]
        for (ii in length(tmp_path):1){
          ptemp <- tmp_path[ii]
          if(ii == length(tmp_path)){
            modifier[ptemp] <- list(value[i]) # `NULL`-compatible assignment
          } else {
            modifier[[ptemp]] <- modifier
            modifier[[1]] <- NULL
          }
        }
        output %<>%
          purrr::list_modify(!!!modifier)
      }
    ### Single Insert
    } else {
      modifier <- list()
      for (i in length(path):1) {
        ptemp = path[i]
        if (i == length(path)) {
          modifier[ptemp] <- list(value[1]) # `NULL`-compatible assignment
        } else {
          modifier[[ptemp]] = modifier
          modifier[[1]] <- NULL
        }
      }
      output %<>%
        purrr::list_modify(!!!modifier)
    }
  }
  # Final return
  return(output)
}

list_path_preexists <- function(lst, path){
  # Create object to hold info
  preexists <- rep(FALSE, length(path))
  # Return where nothing to evaluate
  if(is.null(lst)) return(preexists)
  # Assure expected data type
  #assertive.types::assert_is_list(lst)
  # Generate temp object to hold content of increasing depth
  tmp_lst <- lst
  # Iterate over path
  for (lvi in seq_along(path)){
    ## Retrieve path item
    lv <- path[[lvi]]
    ## No further evaluation if not path item not in names - branch tip reached.
    if(!(lv %in% names(tmp_lst))) break()
    ## Indicate preixistence
    preexists %<>%
      magrittr::inset2(lvi, TRUE)
    ## Assure that non-tip entry is a list to add to
    if(lvi != length(path) && !is.list(tmp_lst)) stop('Preexisting non-tip entry is NOT a list:', lv)
    ## Descent further into lst
    tmp_lst %<>%
      magrittr::extract2(lv)
  }
  # Return result
  return(preexists)
}