When I pass a data.table as an argument to a function, I can update that table 'by reference' within the called function and the results are applied to the original object. However if I do something that requires a 'deep copy' (e.g. rbindlist to add rows) the copy exists only in the called function. The original object remains in the calling frame unchanged.
library(data.table)
l1 <- function(a1, action='update'){
b <- l2(a1, action)
print('l1')
print(a1)
}
l2 <- function(a2, action){
c <- l3(a2, action)
print('l2')
print(a2)
}
l3 <- function(a3, action){
if (action == 'update') a3[, col2 := col + 1]
if (action == 'append') a3 <- rbindlist(list(a3, data.table(col = c(21, 22))), fill=TRUE)
if (action == 'forceupdate') assign('DT',
rbindlist(list(a3, data.table(col = c(21, 22))), fill=TRUE),
envir = parent.frame(3))
print('l3')
print(a3)
a3
}
DT <- data.table(col = c(1, 2, 3))
print(DT)
#> col
#> 1: 1
#> 2: 2
#> 3: 3
l1(DT, 'update')
#> [1] "l3"
#> col col2
#> 1: 1 2
#> 2: 2 3
#> 3: 3 4
#> [1] "l2"
#> col col2
#> 1: 1 2
#> 2: 2 3
#> 3: 3 4
#> [1] "l1"
#> col col2
#> 1: 1 2
#> 2: 2 3
#> 3: 3 4
print(DT)
#> col col2
#> 1: 1 2
#> 2: 2 3
#> 3: 3 4
l1(DT, 'append')
#> [1] "l3"
#> col col2
#> 1: 1 2
#> 2: 2 3
#> 3: 3 4
#> 4: 21 NA
#> 5: 22 NA
#> [1] "l2"
#> col col2
#> 1: 1 2
#> 2: 2 3
#> 3: 3 4
#> [1] "l1"
#> col col2
#> 1: 1 2
#> 2: 2 3
#> 3: 3 4
print(DT)
#> col col2
#> 1: 1 2
#> 2: 2 3
#> 3: 3 4
l1(DT, 'forceupdate')
#> [1] "l3"
#> col col2
#> 1: 1 2
#> 2: 2 3
#> 3: 3 4
#> [1] "l2"
#> col col2
#> 1: 1 2
#> 2: 2 3
#> 3: 3 4
#> [1] "l1"
#> col col2
#> 1: 1 2
#> 2: 2 3
#> 3: 3 4
print(DT)
#> col col2
#> 1: 1 2
#> 2: 2 3
#> 3: 3 4
#> 4: 21 NA
#> 5: 22 NA
Created on 2021-04-22 by the reprex package (v1.0.0)
In this example there is a 3-level stack of function calls. The argument at the first level is passed down the stack and updated in function l3.
Using the data.table update syntax, l3 adds a new column to the object and this results in the original object being changed 'in place' and the results are seen in each level in the calling stack.
However if I add rows, using rbindlist, a copy is made within the frame of l3 and this does not affect the original object, or any view of this in the parent calls.
If I assign the change back to the 'original frame' then it is seen there, but the intervening calls don't see the change.
Is there a way of reflecting the results of this 'deep copy' back up the calling stack?
If assign
is the way to go, I would appreciate an example for how to establish the name and environment of the underlying data object so that this assignment can be made without hard coding.