New Answer
Based on advice from @Frank and this section of the vignette I can't believe I hadn't read before, here's a solution to this problem that doesn't allow arbitrary code to be executed.
library(data.table)
dt = data.table(x=1:3, y=2:4)
x = "y"
ExecuteMeLater = substitute(3*x, list(x=as.symbol(x)))
dt[, eval(ExecuteMeLater)]
# [1] 6 9 12
This behavior in particular is why I prefer this solution:
x = "(system(paste0('kill ',Sys.getpid())))"
ExecuteMeLater = substitute(3*x, list(x=as.symbol(x)))
dt[, eval(ExecuteMeLater)]
#Error in eval(jsub, SDenv, parent.frame()) :
# object '(system(paste0('kill ',Sys.getpid())))' not found
Original Answer
Note: came across what looks like a really useful resource for questions of this nature... might be able to update with a less hacky solution at some point.
The get()
behavior certainly leaves the door open for unexpected outcomes, and it appears this has been brought up in more than a few some github issues in the past. To be frankly honest I've done a decent amount of investigation but I'm still not quite following exactly what the proper usage would be.
One way you can work around it is by pasting together the expression and evaluating your function input column names outside of the data.table
environment and storing it as a character.
Then, by parsing and evaluating the pre-constructed expression in the data.table
environment we avoid any opportunity for a column named x
within the table to take precedence over the contents of the variable x
.
library(data.table)
dt = data.table(x=1:3, y=2:4)
x = 'y'
ExecuteMeLater <- paste0("3*",x) ## "3*y"
dt[, eval(parse(text = ExecuteMeLater))]
Output:
[1] 6 9 12
Not the prettiest solution, but it's worked for me numerous times in the past.
Quick disclaimer on hypothetical doomsday scenarios possible with eval(parse(...))
There are far more in depth discussions on the dangers eval(parse(...))
, but I'll avoid repeating them in full.
Theoretically you could have issues if one of your columns is named something unfortunate like "(system(paste0('kill ',Sys.getpid())))"
(Do not execute that, it will kill your R session on the spot). This is probably enough of an outside chance to not lose sleep over it unless you plan on putting this in a package on CRAN.