Представление дерева каталогов в виде рекурсивного списка

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

Вывод должен иметь вид myList$dir$subdir$subdir$fullFilePath

Итак, в основном я хочу представить дерево каталогов в виде определенного списка. Я получаю все файлы, получаю все подкаталоги для каждого файла, но я застрял в том, как бросить все это в список с несколькими уровнями.


person Karolis Koncevičius    schedule 07.01.2013    source источник


Ответы (3)


Вот решение с использованием рекурсии:

tree.list <- function(file.or.dir) {
    isdir <- file.info(file.or.dir)$isdir
    if (!isdir) {
        out <- file.or.dir
    } else {
        files <- list.files(file.or.dir, full.names   = TRUE,
                                         include.dirs = TRUE)
        out <- lapply(files, tree.list)
        names(out) <- basename(files)
    }
    out
}

Я проверил это здесь, в небольшом каталоге

test.dir <- tree.list("./test")
test.dir
# $a
# $a$`1.txt`
# [1] "./test/a/1.txt"
# 
# $a$aa
# $a$aa$`2.txt`
# [1] "./test/a/aa/2.txt"
# 
# $b
# $b$`3.txt`
# [1] "./test/b/3.txt"

Если это слишком медленно для ваших нужд, я бы рассмотрел возможность чтения всех файлов в один вызов list.files с recursive = TRUE, а затем немного проанализировал.

person flodel    schedule 07.01.2013
comment
Спасибо! это так элегантно, что больно. - person Karolis Koncevičius; 07.01.2013
comment
Пока это не будет слишком медленно. А насчет парсинга - я бы еще понятия не имел, как сделать рекурсивный список после парсинга. после strsplit у меня будет что-то вроде: list(dir=myDir, subdir1=mySubdir1, subdir2=mySubdir2, file=myFile). Цикл for не будет работать с файлом list[[myDir]][[mySubdir1]][[mySubdir2]][[myFile]] ‹-, так как я не могу указать желаемое количество уровней в цикле. Еще раз спасибо за этот ответ. - person Karolis Koncevičius; 07.01.2013

Вот уродливый хак.

mypath <- 'a/b/c/d'


makelist <- function(filepath, fsep = '/'){

  unlisted <- unlist(strsplit(filepath, fsep))

  nsubs <- length(unlisted)

  mylistcall <- paste(paste(rep('list(', nsubs), unlisted, collapse = '='), 
    '= NULL', paste(rep(')', nsubs), collapse = ''))


  mylist <- eval(parse(text = mylistcall))
  return(mylist)
  }

makelist(mypath)

$a
$a$b
$a$b$c
$a$b$c$d
NULL   

Вспоминая

fortune(106)

If the answer is parse() you should usually rethink the question.
   -- Thomas Lumley
      R-help (February 2005)

Однако в этом случае я бы сказал, что мне следует переосмыслить ответ.

person mnel    schedule 07.01.2013
comment
Спасибо! flodel придумал более аккуратный ответ. Если бы это было не так, я бы с радостью принял ваш ответ. - person Karolis Koncevičius; 07.01.2013
comment
Кстати, только что заметил - что это за fortune штука у тебя собирается? - person Karolis Koncevičius; 18.11.2014
comment
@Каролис Концевичюс. см. cran.r-project.org/web/packages/fortunes/index .html - person mnel; 19.11.2014

Вот более короткий вариант замечательного решения @flodel с использованием пакета purrr:

library( purrr )
tree_list <- function( file_or_dir ) {
  f <- partial(list.files, full.names=TRUE, include.dirs=TRUE) %>%
         compose(tree_list, .)
  file_or_dir %>% set_names( basename(.) ) %>% map_if(dir.exists, f)
}

Первая строка определяет функцию f, которая расширяет свой аргумент, используя list.files( ..., full.names=TRUE, include.dirs=TRUE), а затем применяет tree_list() к расширению.

Вторая строка применяет определенную функцию f ко всем каталогам в исходном аргументе.

person Artem Sokolov    schedule 29.10.2018
comment
Жаль, что я видел это, прежде чем сделать это трудным путем! - person Nate Lockwood; 20.12.2019