Reinhold Kainhofer
Reinhold Kainhofer

Reputation: 340

Accessing ... function arguments by (string) name inside the function in R?

I'm trying to write a function with dynamic arguments (i.e. the function argument names are not determined beforehand). Inside the function, I can generate a list of possible argument names as strings and try to extract the function argument with the corresponding name (if given). I tried using match.arg, but that does not work.

As a (massively stripped-down) example, consider the following attempt:

# Override column in the dataframe. Dots arguments can be any 
# of the column names of the data.frame.
dataframe.override = function(frame, ...) {
  for (n in names(frame)) {
    # Check whether this col name was given as an argument to the function
    if (!missing(n)) {
      vl = match.arg(n);
      # DO something with that value and assign it as a column:
      newval = vl
      frame[,n] = newval
    }
  }
  frame
}

AA = data.frame(a = 1:5, b = 6:10, c = 11:15)
dataframe.override(AA, b = c(5,6,6,6,6)) # Should override column b

Unfortunately, the match.arg apparently does not work:

Error in match.arg(n) : 'arg' should be one of

So, my question is: Inside a function, how can I check whether the function was called with a given argument and extract its value, given the argument name as a string?

Thanks, Reinhold

PS: In reality, the "Do something..." part is quite complicated, so simply assigning the vector to the dataframe column directly without such a function is not an option.

Upvotes: 0

Views: 565

Answers (1)

Michael Griffiths
Michael Griffiths

Reputation: 1427

You probably want to review the chapter on Non Standard Evaluation in Advanced-R. I also think Hadley's answer to a related question might be useful.

So: let's start from that other answer. The most idiomatic way to get the arguments to a function is like this:

get_arguments <- function(...){
    match.call(expand.dots = FALSE)$`...`
}

That provides a list of the arguments with names:

> get_arguments(one, test=2, three=3)
[[1]]
one

$test
[1] 2

$three
[1] 3

You could simply call names() on the result to get the names.

Note that if you want the values as strings you'll need to use deparse, e.g.

deparse(get_arguments(one, test=2, three=3)[[2]])
[1] "2"

P.S. Instead of looping through all columns, you might want to use intersect or setdiff, e.g.

dataframe.override = function(frame, ...) {
  columns = names(match.call(expand.dots = FALSE)$`...`)[-1]
  matching.cols <- intersect(names(frame), names(columns))
  for (i in seq_along(matching.cols) {
    n = matching.cols[[i]]
    # Check whether this col name was given as an argument to the function
    if (!missing(n)) {
      vl = match.arg(n);
      # DO something with that value and assign it as a column:
      newval = vl
      frame[,n] = newval
    }
  }
  frame
}

P.P.S: I'm assuming there's a reason you're not using dplyr::mutate for this.

Upvotes: 1

Related Questions