I found a way to get it work, but I am not sure if I took the right approach or if there are easier ways to deal with ... in my scenario.
Consider a few silly functions that update a data.table by reference. Note all is about the wrapper, not about what my test functions actually do. Yes in practice they all update x by reference, just like my examples.
Then consider a wrapper function that has as input the data.table and a vector with functions to call one by one. All these functions rely on x and that is always a data.table. The other arguments differ per function, but all have defined default values.
the functions to play with that need to accessed by the wrapper
change_rep <- function(x, length = 3L, multiplier = 1L) {
x[, col_4 := paste(rep(col_2, (length * multiplier)), sep = "", collapse = ""), col_2]
}
change_case <- function(x, to = "lower") {
if (to %in% c("upper", "lower", "title")) {
func <- paste0("stri_trans_to", to)
x[, col_5 := match.fun(func)(col_4)]
} else {
stop(paste0("check the \"to\" argument, \"", to, "\" is not any of c(\"upper\", \"lower\", \"title\")"), call. = F)
}
}
Now I want a wrapper that allows me to once supply x, tell which functions to apply using a what argument which is a vector. All optional arguments used by the functions called by the wrapper are in the .... The tricky thing here is that if you call a function with arguments that the function does not know, it throws an error. Below the wrapper I have this far and it seems to work fine, but I wonder if this is the most efficient way to handle this and if perhaps I should include error/warning handling for situations where an argument is provided that is not used in any of the functions. For example lengthhhh = 10L would now be silently ignored, while probably one meant length = 10L that would only be used in one of the functions. I highly commented the wrapper.
change_wrapper <- function(x, what, ...) {
# define the change_* functions we have available
supported <- c("case", "rep")
# check for what values we have no function for and show them in a warning
if (!identical(setdiff(what, supported), character(0))) {
warning("Check the \"what\" argument. These unsupported values were found and ignored: \"", paste(setdiff(what, supported), collapse = ", "), "\".", call. = F)
}
# ignore those above by removing unsupported ones from "what"
what <- intersect(what, supported)
# check if "what" still has values, if not throw an error
if (identical(what, character(0))) {
stop("Check the \"what\" argument. At least one of \"", paste(supported, collapse = ", "), "\" must be provided.", call. = F)
}
# grab the provided ... arguments, note that x is not in this list!
args <- list(...)
# loop over the functions we want to call
for (w in what) {
# create the function name to call
func <- paste("change", w, sep = "_")
# remember ... can contain arguments that are not supported in all of the functions
# prevent any "Error in *(...) : unused argument
arguments <- args[names(args) %in% names(formals(match.fun(func)))]
# add x as it is not part of ...
arguments$x = x
# call the correct function and provide the arguments supported
do.call(match.fun(func), arguments)
}
}
data and some examples
library(data.table)
library(stringi)
x <- data.table(
col_1 = letters[1:10],
col_2 = c(letters[1:5], LETTERS[1:5]),
col_3 = 1:10
)
x
# col_1 col_2 col_3
# 1: a a 1
# 2: b b 2
# 3: c c 3
# 4: d d 4
# 5: e e 5
# 6: f A 6
# 7: g B 7
# 8: h C 8
# 9: i D 9
# 10: j E 10
change_wrapper(x, what = c("rep", "case", "FOO"), to = "title", length = 2L, multiplier = 2L, blalalala = F)
# Warning message:
# Check the "what" argument. These unsupported values were found and ignored: "FOO".
x[]
# col_1 col_2 col_3 col_4 col_5
# 1: a a 1 aaaa Aaaa
# 2: b b 2 bbbb Bbbb
# 3: c c 3 cccc Cccc
# 4: d d 4 dddd Dddd
# 5: e e 5 eeee Eeee
# 6: f A 6 AAAA Aaaa
# 7: g B 7 BBBB Bbbb
# 8: h C 8 CCCC Cccc
# 9: i D 9 DDDD Dddd
# 10: j E 10 EEEE Eeee
change_wrapper(x, what = c("BOO", "FOO"))
# Error: Check the "what" argument. At least one of "case, rep" must be provided.
# In addition: Warning message:
# Check the "what" argument. These unsupported values were found and ignored: "BOO, FOO".