How to check existence of an input argument for R functions
Asked Answered
G

4

54

I have a function defined as

myFun <- function(x, y, ...) {
  # using exists
  if (exists("z")) { print("exists z!") }
  # using missing
  try(if (!missing("z")) { print("z is not missing!") }, silent = TRUE)
  # using get
  try(if (get("z")) { print("get z!") }, silent = TRUE)

  # anotherFun(...)
}

In this function, I want to check whether user input "z" in the argument list. How can I do that? I tried exists("z"), missing("z"), and get("z") and none of them works.

Gyimah answered 26/3, 2012 at 18:2 Comment(5)
Can you show exactly how you've used missing? Because AFAIK that's the correct function to use.Linger
It would help if you provide a bit more context. There may be a better way to do what you're trying to accomplish.Lingenfelter
@joran, missing() only applies for argument. Here there is no argument z, it can only be entered as part of ...Violate
@SachaEpskamp I agree. I simply wasn't sure if what the OP wrote was actually what they were doing.Linger
I've modified the code in the question to make it easier to understand. Thanks for the comments.Gyimah
C
35

@Sacha Epskamp has a pretty good solution, but it doesn't always work. The case where it fails is if the "z" argument is passed in as NULL...

# Sacha's solution
myFun <- function(x, y, ...) { 
  args <- list(...)
  exist <- !is.null(args[['z']])
  return(exist)
}

myFun(x=3, z=NULL) # FALSE, but should be TRUE!


# My variant
myFun2 <- function(x, y, ...) {
  args <- list(...)
  exist <- "z" %in% names(args)
  exist
}

myFun2(x=3, z=NULL) # TRUE
Compony answered 26/3, 2012 at 18:34 Comment(0)
D
85

I think you're simply looking for hasArg

myFun <- function(x, y, ...) { 
  hasArg(z)
}

> myFun(x=3, z=NULL)
[1] TRUE

From ?hasArg:

The expression hasArg(x), for example, is similar to !missing(x), with two exceptions. First, hasArg will look for an argument named x in the call if x is not a formal argument to the calling function, but ... is. Second, hasArg never generates an error if given a name as an argument, whereas missing(x) generates an error if x is not a formal argument.

Dupaix answered 27/3, 2012 at 20:4 Comment(0)
C
35

@Sacha Epskamp has a pretty good solution, but it doesn't always work. The case where it fails is if the "z" argument is passed in as NULL...

# Sacha's solution
myFun <- function(x, y, ...) { 
  args <- list(...)
  exist <- !is.null(args[['z']])
  return(exist)
}

myFun(x=3, z=NULL) # FALSE, but should be TRUE!


# My variant
myFun2 <- function(x, y, ...) {
  args <- list(...)
  exist <- "z" %in% names(args)
  exist
}

myFun2(x=3, z=NULL) # TRUE
Compony answered 26/3, 2012 at 18:34 Comment(0)
C
10

There might be instances when you might not want to call list(...), since this will evaluate all the expressions in the dots. For example,

myFun <- function(x, y, ...){
  myArgs <- list(...)
  zInArgs <- ("z" %in% names(myArgs))
  return(zInArgs)
}

myFun(x = 2, y = "Happy", z = list(rep(rnorm(2e6), 100)))

This will take a long time. Instead, use match.call():

myFun <- function(x, y, ...){
  myArgs <- match.call()
  zInArgs <- ("z" %in% names(myArgs))
  return(zInArgs)
}

myFun(x = 2, y = "Happy", z = list(rep(rnorm(2e6), 100)))

The first example is still chugging away on my machine, while the second example should take practically no time at all.

EDIT:

To answer the comment from @CarlWitthoft:

R> system.time(
+   (myAns <- myFun(x = 2, y = "Happy", z = list(rep(rnorm(2e6), 100))))
+ )
   user  system elapsed 
      0       0       0 
R> myAns
[1] TRUE
Chumash answered 27/3, 2012 at 4:40 Comment(3)
For completeness' sake, can you report on the time it does take to run the second example?Smelly
Hi @CarlWitthoft, please see my edit above. Do you get differing results?Chumash
well, I get NULL for a result because I'm not allowed (yet) to install R at work :-(. So, thanks for doing the timing tests.Smelly
V
5

Here is a way I often do it. First convert ... to a list, then check if the elements are not NULL:

myFun <- function(x, y, ...) { 
args <- list(...)
exist <- !is.null(args[['z']])
return(exist)
}

Some results:

> myFun()
[1] FALSE
> myFun(z=1)
[1] TRUE
Violate answered 26/3, 2012 at 18:15 Comment(5)
I don't think this is the intended way ... is supposed to be used though. It is more a way of passing arguments to another function, but this is how I currently use it in my package as well (I really should change that:))Violate
Sacha, I do agree with you. If "z" is used in this function, it should not be in the ... But very often code is already written and making one change in the argument list can introduce errors that requires many changes to fix. So this is my lazy way to make it work. :)Gyimah
I added an alternative solution that also handles myFun(z=NULL)Compony
@Gyimah Also this gives you awesome hidden and undocumented arguments that only you know about!Violate
@danioyuan: the safe, and possibly "proper," way to add an argument is to place it last in the list with a default value assigned, i.e. myfunc(x,y,...) becomes myfunc(x,y,z=1,...) . AFAIK that will provide full back-compatibility.Smelly

© 2022 - 2024 — McMap. All rights reserved.