I am trying to write a function that takes in package names as arguments, and then checks whether they are installed or not one at a time. If they are not installed, then the function will install the packages and subsequently load them into the current session. If they are installed, then the function will just load the packages into the current session.
packin <- function(...) {
packlist <- list(...)
for(i in 1:length(packlist)) {
if(!requireNamespace(packlist[i])) {
install.packages(packlist[i])
library(packlist[i])
} else {
library(packlist[i])
}
}
}
I tried this with two packages: tidyverse (installed but not loaded) and writexl (neither installed nor loaded).
packin('tidyverse', 'writexl')
But this produces the error: Error in library(packlist[i]) : 'package' must be of length 1
. However, when I check the length of a list of both packages for one item in the list, the length is equal to 1.
packlist <- list('tidyverse', 'writexl')
length(packlist[1]) == 1
length(packlist[1]) == 1
returns TRUE
. So what am I doing wrong or not understanding about the error that the 'package' must be of length 1 when it is telling me that is of length 1?
You need to use character.only = TRUE
when passing anything other than a literal string to `library():
p <- list("writexl", "tidyverse")
library(p[1]) ## Error in library(p) : there is no package called ‘p’
library(p[1], character.only = TRUE)
The reason for the obscure error message is that library()
internally calls:
if (!character.only) package <- as.character(substitute(package))
which evaluates (weirdly) to "[" "p" "1"
in this case (or "[" "packlist" "i"
if the input is packlist[i]
)
You can probably do this slightly more compactly:
packin <- function(...) {
packlist <- unlist(list(...))
ip <- rownames(installed.packages())
to_install <- setdiff(packlist, ip)
install.packages(to_install, character.only = TRUE)
sapply(packlist, library, character.only = TRUE)
}
The pacman
package has a lot of useful functions for this kind of thing.