2013-05-21 2 views
2

Есть ли элегантный способ в R, чтобы рекурсивно превратить произвольный глубокий список (содержащий списки и векторы) в вектор путей? Например, преобразовать это:Рекурсивный объект-листинг

list(
    home = list(
    jerry = c("R", "bla"), 
    mary = "xx" 
), 
    tmp = c("foo", "bar"), 
    c("etc") 
) 

Для объекта, как это:

c(
    "/home/jerry/R", 
    "/home/jerry/bla", 
    "/home/mary/xx", 
    "/tmp/foo", 
    "/tmp/bar", 
    "/etc" 
) 

ответ

1

Имена в unlist сделать примерно то, что вы хотите:

> test <- list(
+  home = list(
+   jerry = c("R", "bla"), 
+   mary = "xx" 
+ ), 
+  tmp = c("foo", "bar"), 
+  etc = c() 
+) 
> unlist(test) 
home.jerry1 home.jerry2 home.mary  tmp1  tmp2 
     "R"  "bla"  "xx"  "foo"  "bar" 

Ручки несколько уровней рекурсии, а также :

> test <- list(
+  home = list(
+   jerry = list(a="R", b="bla"), 
+   mary = list(c="xx") 
+ ), 
+  tmp = list(d="foo", e="bar"), 
+  etc = list(nothing=NULL) 
+) 
> unlist(test) 
home.jerry.a home.jerry.b home.mary.c  tmp.d  tmp.e 
     "R"  "bla"   "xx"  "foo"  "bar" 

Оттуда легко добавить последние немного вы хотите (с окончательным значением будет последним путем elemtn) на:

> unl <- unlist(test) 
> res <- names(unl) 
> res <- paste(res,unl,sep=".") 
> res 
[1] "home.jerry.a.R" "home.jerry.b.bla" "home.mary.c.xx" "tmp.d.foo"  "tmp.e.bar"  
4

Пока никто из списка ваших элементов не имеет имен, которые включают в себя точку (и исключение из этого беспокойного NULL значных etc элемента в списке), это должно работать:

ll <- rapply(l, function(X) sapply(X,list), how="replace") #for tip element names 
nms <- names(unlist(ll)) 
gsub(".", "/", nms, fixed=TRUE) 
# [1] "home/jerry/R" "home/jerry/bla" "home/mary/xx" "tmp/foo"  
# [5] "tmp/bar"  
+0

Спасибо. Я изменил 'etc' как вектор вместо имени. Теперь он отлично работает. – Jeroen

3

Для более общего подхода, в том числе posdibly имен с точками и нулевыми элементами, используйте этот:

f <- function(test, parent=character(0)) 
{ 
    if(is.null(test)) return(parent) 

    if(is.list(test)) 
    { 
     result <- character(0) 

     for(i in seq_along(test)) 
     { 
      result <- c(result, Recall(test[[i]], parent=paste0(parent,"/",names(test)[i]))) 
     } 
    } 
    else 
    { 
     result <- paste0(parent,"/",as.character(test)) 
    } 

    result 
} 

f(test) 

#[1] "/home/jerry/R" "/home/jerry/bla" "/home/mary/xx" "/tmp/foo"  "/tmp/bar"  "/etc" 
+0

Прохладный. Большое использование «Recall» для рекурсивного вызова анонимной функции. – Jeroen

+0

Супер. Спасибо за это. Я уверен, что буду использовать его в качестве шаблона для других функций древовидной ходьбы в будущем. –

+0

:-) @ JoshO'Brien, основная идея состоит в том, чтобы иметь аргумент, чтобы отслеживать уровень вашей глубины (по умолчанию - нуль или нуль или что-то подобное). –