Реструктуризация произвольно вложенного списка

#r #lapply #purrr

Вопрос:

У меня есть список со следующей структурой:

   l <- list(condition = "AND", 
            rules = list(list(id = "sex", 
                              field = "sex"),
                         list(condition = "AND", 
                              rules = list(
                                list(id = "species",
                                     field = "species"), 
                                list(condition = "AND",
                                     rules = list(
                                       list(id = "bill_length_mm", 
                                            field = "bill_length_mm")))))), 
            valid = TRUE)
 

и я хотел бы перейти на эту структуру

   goal <- list(
    list(id = "sex",
         field = "sex"),
    list(id = "species",
         field = "species"),
    list(id = "bill_length_mm", 
         field = "bill_length_mm")
    )
 

Решение должно быть достаточно гибким, чтобы обрабатывать больше/меньше rules элементов. Любые предложения о том, как я могу это сделать, будут высоко оценены!

Ответ №1:

Похоже, здесь вы могли бы использовать рекурсивную стратегию. Это приведет к поиску списков, содержащих id элемент, и вернет их в виде списка.

 get_fields <- function (x) {
  if (is.list(x)) {
    if (!is.null(x$id)) {
      return(list(x))
    } else {
      unname(do.call("c", lapply(x, get_fields)))
    }
  } else {
    NULL
  }
}
dput(result<-get_fields(l))
# list(list(id = "sex", field = "sex"), list(id = "species", field = "species"), 
#    list(id = "bill_length_mm", field = "bill_length_mm"))
 

Ответ №2:

Использование рекурсивной функции с rrapply

 library(rrapply)
out <- rrapply(l, condition = function(x, .xname)
         .xname %in% c('id', 'field'), how = "flatten" )

out2 <- unname(split(as.list(out), cumsum(names(out) == "id")) )
 

-проверка с ожидаемым результатом

 > identical(goal, out2)
[1] TRUE
 

Или, как упоминал @Joris C. в комментариях

 out <- rrapply(l, classes = "list", condition = 
    function(x) "id" %in% names(x), how = "flatten")
names(out) <- NULL
 

Комментарии:

1. Немного более компактный способ получить тот же результат: out <- rrapply(l, classes = "list", condition = function(x) "id" %in% names(x), how = "flatten"); names(out) <- NULL . Здесь classes = "list" применяется f также к элементам (под)списка, если condition выполняется.