Oh for {fs}, are you kidding me, base R?

r
Published

January 5, 2023

This is a cautionary tale about the base R function list.files(). When you give it a vector of paths (which you totally can do), it re-orders the output alphabetically. This screwed me over in a project I’m working on where I took file paths from a data frame, used them to list.files() and assumed (a sane assumption, I think) that the output would be in the same order. In this case, I knew there was only one file per path, but I think I would have assumed this even if it was returning more than one file.

tmp <- tempdir()
dir.create(file.path(tmp, "A"))
dir.create(file.path(tmp, "B"))
dir.create(file.path(tmp, "C"))
file.create(file.path(tmp, "A", "A.txt"))
[1] TRUE
file.create(file.path(tmp, "B", "B.txt"))
[1] TRUE
file.create(file.path(tmp, "C", "C.txt"))
[1] TRUE
file_list <- file.path(tmp, c("C", "A", "B"))
file_list #in order C, A, B
[1] "/var/folders/wr/by_lst2d2fngf67mknmgf4340000gn/T//RtmpYfrDkf/C"
[2] "/var/folders/wr/by_lst2d2fngf67mknmgf4340000gn/T//RtmpYfrDkf/A"
[3] "/var/folders/wr/by_lst2d2fngf67mknmgf4340000gn/T//RtmpYfrDkf/B"
list.files(file_list, full.names = TRUE) #in order A, B, C!
[1] "/var/folders/wr/by_lst2d2fngf67mknmgf4340000gn/T//RtmpYfrDkf/A/A.txt"
[2] "/var/folders/wr/by_lst2d2fngf67mknmgf4340000gn/T//RtmpYfrDkf/B/B.txt"
[3] "/var/folders/wr/by_lst2d2fngf67mknmgf4340000gn/T//RtmpYfrDkf/C/C.txt"

So I was wrong, and it made all the work I did for the past several months somewhat wrong, but the good news is there is an easy fix. The fs package is the ‘tidy’ solution to working with files and file paths in R. The fs alternative to list.files() is dir_ls(), and like many tidyverse equivalents of base R functions, it is better because it does less. It won’t re-order the outputs and it always assumes you want the full paths (not just the file name as is the default with list.files()).

library(fs)
fs::dir_ls(file_list) #in correct order C, A, B
/var/folders/wr/by_lst2d2fngf67mknmgf4340000gn/T/RtmpYfrDkf/C/C.txt
/var/folders/wr/by_lst2d2fngf67mknmgf4340000gn/T/RtmpYfrDkf/A/A.txt
/var/folders/wr/by_lst2d2fngf67mknmgf4340000gn/T/RtmpYfrDkf/B/B.txt

Needless to say, I’ll be switching over to fs::dir_ls() for this project. I’ll also be spending some more time exploring the fs package and likely using it for all my file exploring and manipulation needs from now on.