178

I am looking for the reverse of get().

Given an object name, I wish to have the character string representing that object extracted directly from the object.

Trivial example with foo being the placeholder for the function I am looking for.

z <- data.frame(x=1:10, y=1:10)

test <- function(a){
  mean.x <- mean(a$x)
  print(foo(a))
  return(mean.x)}

test(z)

Would print:

  "z"

My work around, which is harder to implement in my current problem is:

test <- function(a="z"){
  mean.x <- mean(get(a)$x)
  print(a)
  return(mean.x)}

test("z")
MichaelChirico
  • 33,841
  • 14
  • 113
  • 198
Etienne Low-Décarie
  • 13,063
  • 17
  • 65
  • 87

4 Answers4

200

The old deparse-substitute trick:

a<-data.frame(x=1:10,y=1:10)
test<-function(z){
   mean.x<-mean(z$x)
   nm <-deparse(substitute(z))
   print(nm)
   return(mean.x)}
 
 test(a)
#[1] "a"   ... this is the side-effect of the print() call
#          ... you could have done something useful with that character value
#[1] 5.5   ... this is the result of the function call

Edit: Ran it with the new test-object

Note: this will not succeed inside a local function when a set of list items are passed from the first argument to lapply (and it also fails when an object is passed from a list given to a for-loop.) You would be able to extract the ".Names"-attribute and the order of processing from the structure result, if it were a named vector that were being processed.

> lapply( list(a=4,b=5), function(x) {nm <- deparse(substitute(x)); strsplit(nm, '\\[')} )
$a      # This "a" and the next one in the print output are put in after processing
$a[[1]]
[1] "X"    ""     "1L]]"  # Notice that there was no "a"


$b
$b[[1]]
[1] "X"    ""     "2L]]"

> lapply( c(a=4,b=5), function(x) {nm <- deparse(substitute(x)); strsplit(nm, '\\[')} )
$a
$a[[1]]   # but it's theoretically possible to extract when its an atomic vector
[1] "structure(c(4, 5), .Names = c(\"a\", \"b\"))" ""                                            
[3] "1L]]"                                        


$b
$b[[1]]
[1] "structure(c(4, 5), .Names = c(\"a\", \"b\"))" ""                                            
[3] "2L]]"  
IRTFM
  • 258,963
  • 21
  • 364
  • 487
  • 1
    Just a quick note for users of the %>% pipe- this solution will not work properly if the object is piped into the function. Instead, the name will be ".". Took me a while to figure this out. So test(a) works but not a %>% test(). I didn't figure out a way around this and instead added an assertion to prevent piping in. – evanpmorier Aug 24 '23 at 16:09
  • 1
    That’s because you were expecting a non-dplyr/magrittr designed function to behave as if it were designed to be deployed like one. It’s also because you are confusing the new “official” pipe operator that does use the manner of function calling with no argument in the parentheses withe magrittr method that does not use parentheses. If you use `a |> test()`, all is well. – IRTFM Aug 24 '23 at 23:46
  • Many functions that are not tidyverse work fine with objects piped in. I'm also not confusing anything- I specifically cited the magrittr pipe. That said, this is one of the first places I've seen where the base pipe has a clear advantage, so that's good to know. Side note: I was not criticizing your solution, it works nicely. But your tone comes across defensive and aggressive. – evanpmorier Aug 26 '23 at 00:06
  • Your comment was tangential to the problem and the solution; neither had `%>%`. The `%>%` renames piped objects as ".", so it's original name is lost. I suspect that "working fine in t-verse" does not apply to base functions dealing with names of arguments, because it is the nonstandard evaluation blithely assumed in the tidyverse world that prevents new users from understanding how R names and dataframe column names are handled in base R. I admit to getting annoyed when people assume that everyone will have the "tidyverse" loaded without explicitly loading it in their test workspace. – IRTFM Aug 26 '23 at 17:32
27
deparse(quote(var))

My intuitive understanding In which the quote freeze the var or expression from evaluation and the deparse function which is the inverse of parse function makes that freezed symbol back to String


some new comments

for the difference between substitute and quote:

f <- function(x) {
  substitute(x)
}   

and

g = function(x){
  quote(x)
}

The second will always return x (what the quote function get as argument) while the first function will return the object passed to the function, try this:

f(z~x+y) # return you z~x+y
g(z~x+y) # return you x
cloudscomputes
  • 1,278
  • 13
  • 19
  • The second one is somewhat confusing (and perhaps you meant it to be puzzling in an educational way) because there would be no "attempt" by the R interpreter to examine the argument to `quote`. In particular it would not check to see if there were any objects with the name `x` in the search tree. It would not examine the value of `x` or even see whether it existed. – IRTFM Aug 26 '23 at 17:49
8

Note that for print methods the behavior can be different.

print.foo=function(x){ print(deparse(substitute(x))) }
test = list(a=1, b=2)
class(test)="foo"
#this shows "test" as expected
print(test)

#this (just typing 'test' on the R command line)
test
#shows
#"structure(list(a = 1, b = 2), .Names = c(\"a\", \"b\"), class = \"foo\")"

Other comments I've seen on forums suggests that the last behavior is unavoidable. This is unfortunate if you are writing print methods for packages.

Eli Holmes
  • 656
  • 7
  • 10
  • Perhaps it should be: `print.foo=function(x){ cat(deparse(substitute(x))) }` or `print.foo=function(x){ print(deparse(substitute(x)), quote=FALSE) }` – IRTFM Nov 30 '13 at 18:58
  • 1
    Or `print.foo=function(x){ print.default(as.list(x)) }` – IRTFM Nov 30 '13 at 19:05
  • `as.list()` wouldn't work since 'test' could be anything. I just used a list in my toy example. – Eli Holmes Aug 15 '20 at 15:38
  • R 3.6 update. There is now a little bit of a change to the behavior but it is still not fixed. `print(test)` produces `test` while `test` on the command line produces `x` (not `test` as you want). All these have the same behavior. `print.foo=function(x){ print(deparse(substitute(x))) }` or `print.foo=function(x){ cat(deparse(substitute(x))) }` or `print.foo=function(x){ print(deparse(substitute(x)), quote=FALSE) }` – Eli Holmes Aug 15 '20 at 15:39
2

To elaborate on Eli Holmes' answer:

  1. myfunc works beautifully
  2. I was tempted to call it within another function (as discussed in his Aug 15, '20 comment)
  3. Fail
  4. Within a function, coded directly (rather than called from an external function), the deparse(substitute() trick works well.
  5. This is all implicit in his answer, but for the benefit of peeps with my degree of obliviousness, I wanted to spell it out.
an_object <- mtcars
myfunc <- function(x) deparse(substitute(x))

myfunc(an_object)
#> [1] "an_object"

# called within another function 
wrapper <- function(x){
  myfunc(x)
}

wrapper(an_object)
#> [1] "x"
Richard Careaga
  • 628
  • 1
  • 5
  • 11