Search code examples
rscopesubstitutionevaluationnames

From a list of objects, get a character vector of their names


I have a function that takes as an argument a list of functions.

library(moments)
library(plyr)
tests <- list(mean, varience, skewness, kurtosis)
f <- function(X, tests){
  out   <- each(... = tests)(X)   #each from plyr
  names(out) <- GetNames(tests)
  out
}

I want GetNames to take the list of objects, in this case functions, and return the names of the objects as text. Ideally, I'd like GetNames to work with any list of named objects:

> GetNames(tests)
[1] "mean"     "varience" "skewness" "kurtosis"

as.character(tests) returns the text of the code of each function, not their names.

I tried:

GN <- function(X) deparse(substitute(X))
GetNames <- function(X) lapply(tests, GN)
GetNames(tests)

But this returns:

[[1]]
[1] "X[[i]]"

[[2]]
[1] "X[[i]]"

[[3]]
[1] "X[[i]]"

[[4]]
[1] "X[[i]]"

I have some version of this problem frequently when writing R code. I want a function to evaluate its argument some number of steps, here one step from tests to the names of its objects, and then stop and let me do something to the result, here convert them to strings, rather than going on to get the referents of the names before I can grab them (the names).


Solution

  • Once you run

    tests <- list(mean, varience, skewness, kurtosis)
    

    those symbols are evaluated and discarded. If you look at

    tests[[2]]
    

    or something, you can see there really isn't an original reference to varience, but rather the funcion that the symbol varience pointed to is now stored in a list. (Things work a bit differently when passing parameters to functions thanks to the promises and the call stack but that's not what you're doing here). There is no lazy-evaluation for list() after you've run it.

    If you want to keep the names of the functions, it's probably best to work with a named list. You can make a helper function like

    nlist <- function(...) { dots<-substitute(...()); setNames(list(...), sapply(dots, deparse))}
    tests <- nlist(mean, var, skewness, kurtosis)
    

    Now the values are preserved as names

    names(tests)
    # [1] "mean"     "var"      "skewness" "kurtosis"