Как управлять элементами NULL во вложенном списке?

У меня есть вложенный список, содержащий элементы NULL, и я хотел бы заменить их чем-то другим. Например:

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

Я хочу заменить NULL-элементы на NA. Естественным способом сделать это является рекурсивный цикл над списком с помощью rapply. Я пробовал:

rapply(l, function(x) NA, classes = "NULL", how = "replace")
rapply(l, function(x) if(is.null(x)) NA else x, how = "replace")

К сожалению, ни один из этих методов не работает, поскольку rapply, по-видимому, игнорирует элементы NULL.

Как я могу манипулировать элементами NULL во вложенном списке?

Ответ 1

Я собираюсь использовать "использование версии rapply, не имеет странного поведения с NULL". Это самая простая реализация, о которой я могу думать:

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

(rawr::rapply2, как упоминалось в комментариях @rawr, является более сложной попыткой.)

Теперь я могу сделать замену, используя

simple_rapply(l, function(x) if(is.null(x)) NA else x)

Ответ 2

Это то, что предложил Уильям Данлэп в 2010 году, когда этот вопрос задавали на 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)

Ответ 3

Это взломать, но, насколько я понимаю, я немного доволен этим.

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:

Если по какой-то причине вам понадобилось "NULL" в качестве символьной записи в списке (например, в углу), вы все равно можете использовать вышеупомянутый взлом, поскольку он заменяет содержимое строки, а не кавычки, поэтому она просто требует еще один шаг

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 

Ответ 4

Я завернул замену внутри sapply, что делает ее более читаемой/понятной для меня, хотя и менее общей.

 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)