How to manipulate NULL elements in a nested list?

前端 未结 5 860
说谎
说谎 2020-12-16 13:19

I have a nested list containing NULL elements, and I\'d like to replace those with something else. For example:

l <- list(
  NULL,
  1,
  list(
    2,
           


        
相关标签:
5条回答
  • 2020-12-16 13:42

    I wrapped the replacement inside the sapply, which makes it more readable/understandable to me, albeit less general.

     replace_null <- function(x) {
      lapply(x, function(x) {
        if (is.list(x)){
          replace_null(x)
          } else{
            if(is.null(x)) NA else(x)
          } 
        })
    }
    
    replace_null(l)
    
    0 讨论(0)
  • 2020-12-16 13:46

    This can also be done with rrapply() in the rrapply-package. Below are a few different ways we could replace the NULL elements in a nested list by NA values:

    library(rrapply)
    
    l <- list(
        NULL,
        1,
        list(
            2,
            NULL,
            list(
                3,
                NULL
            )
        )
    )
    
    ## replace NULL by NA using only f
    rrapply(l, f = function(x) if(is.null(x)) NA else x, how = "replace")
    #> [[1]]
    #> [1] NA
    #> 
    #> [[2]]
    #> [1] 1
    #> 
    #> [[3]]
    #> [[3]][[1]]
    #> [1] 2
    #> 
    #> [[3]][[2]]
    #> [1] NA
    #> 
    #> [[3]][[3]]
    #> [[3]][[3]][[1]]
    #> [1] 3
    #> 
    #> [[3]][[3]][[2]]
    #> [1] NA
    
    ## replace NULL by NA using condition argument
    rrapply(l, condition = is.null, f = function(x) NA, how = "replace")
    #> [[1]]
    #> [1] NA
    #> 
    #> [[2]]
    #> [1] 1
    #> 
    #> [[3]]
    #> [[3]][[1]]
    #> [1] 2
    #> 
    #> [[3]][[2]]
    #> [1] NA
    #> 
    #> [[3]][[3]]
    #> [[3]][[3]][[1]]
    #> [1] 3
    #> 
    #> [[3]][[3]][[2]]
    #> [1] NA
    
    ## replace NULL by NA using condition and deflt arguments 
    rrapply(l, condition = Negate(is.null), deflt = NA, how = "list")
    #> [[1]]
    #> [1] NA
    #> 
    #> [[2]]
    #> [1] 1
    #> 
    #> [[3]]
    #> [[3]][[1]]
    #> [1] 2
    #> 
    #> [[3]][[2]]
    #> [1] NA
    #> 
    #> [[3]][[3]]
    #> [[3]][[3]][[1]]
    #> [1] 3
    #> 
    #> [[3]][[3]][[2]]
    #> [1] NA
    

    We can also prune the NULL elements from the list altogether by setting how = "prune":

    ## keep only non-NULL elements
    rrapply(l, condition = Negate(is.null), how = "prune")
    #> [[1]]
    #> [1] 1
    #> 
    #> [[2]]
    #> [[2]][[1]]
    #> [1] 2
    #> 
    #> [[2]][[2]]
    #> [[2]][[2]][[1]]
    #> [1] 3
    
    0 讨论(0)
  • 2020-12-16 14:02

    This is what William Dunlap suggested in 2010 when this question was asked on Rhelp:

    replaceInList <- function (x, FUN, ...) 
      {
          if (is.list(x)) {
              for (i in seq_along(x)) {
                  x[i] <- list(replaceInList(x[[i]], FUN, ...))
              }
              x
          }
          else FUN(x, ...)
      }
     replaceInList(l, function(x)if(is.null(x))NA else x)
    
    0 讨论(0)
  • 2020-12-16 14:08

    I'm going to go with "use a version of rapply doesn't doesn't have weird behaviour with NULL". This is the simplest implementation I can think of:

    simple_rapply <- function(x, fn)
    {
      if(is.list(x))
      {
        lapply(x, simple_rapply, fn)
      } else
      {
        fn(x)
      }
    }
    

    (rawr::rapply2, as mentioned in the comments by @rawr is a more sophisticated attempt.)

    Now I can do the replacement using

    simple_rapply(l, function(x) if(is.null(x)) NA else x)
    
    0 讨论(0)
  • 2020-12-16 14:08

    This is a hack, but as far as hacks go, I think I'm somewhat happy with it.

    lna <- eval(parse(text = gsub("NULL", "NA", deparse(l))))
    
    str(lna)
    #> List of 3
    #> $ : logi NA
    #> $ : num 1
    #> $ :List of 3
    #> ..$ : num 2
    #> ..$ : logi NA
    #> ..$ :List of 2
    #> .. ..$ : num 3
    #> .. ..$ : logi NA
    

    Update:

    If for some reason you needed "NULL" as a character entry in the list (corner case, much?) you can still use the above hack since it replaces the contents of the string, not the quotes, thus it just requires another step

    l2 <- list(
      NULL,
      1,
      list(
        2,
        "NULL",
        list(
          3,
          NULL
        )
      )
    )
    
    lna2   <- eval(parse(text = gsub("NULL", "NA", deparse(l2))))
    lna2_2 <- eval(parse(text = gsub('\\"NA\\"', '\"NULL\"', deparse(lna2))))
    
    str(lna2_2)
    #> List of 3
    #> $ : logi NA
    #> $ : num 1
    #> $ :List of 3
    #> ..$ : num 2
    #> ..$ : chr "NULL"
    #> ..$ :List of 2
    #> .. ..$ : num 3
    #> .. ..$ : logi NA 
    
    0 讨论(0)
提交回复
热议问题