Skip to content

Commit e32e553

Browse files
Refactored duplicate recursive assignment logic in [.data.table and setDT into shared helper (#7064)
* added a helper function 6702 * added nocov * removed caller * rm ws diff * nocov * not nocov * message change * . * .. * updated logic * use a less generic name * switch to %iscall% * mention 'i' for more clarity * better comment * style nit * attempt to simplify (i'm probably wrong) * rm parent.frame() usage * needs to be parent.frame(2) i think... * save a line+variable * final tweak * OK final-v4-for-real.docx * changed my mind * update test error msg * combine old & new tests * need data.table() to restore alloccol message consistency --------- Co-authored-by: Michael Chirico <[email protected]>
1 parent 625250e commit e32e553

File tree

2 files changed

+30
-33
lines changed

2 files changed

+30
-33
lines changed

R/data.table.R

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,27 @@ replace_dot_alias = function(e) {
128128
}
129129
}
130130

131+
.reassign_extracted_table = function(name, value, env = parent.frame(2L)) {
132+
k = eval(name[[2L]], env, env)
133+
if (is.list(k)) {
134+
origj = j = if (name %iscall% "$") as.character(name[[3L]]) else eval(name[[3L]], env, env)
135+
if (length(j) != 1L) {
136+
stopf("Invalid set* operation on a recursive index L[[i]] where i has length %d. Chain [[ instead.", length(j))
137+
}
138+
if (is.character(j)) {
139+
j = match(j, names(k))
140+
if (is.na(j)) {
141+
stopf("Item '%s' not found in names of input list", origj)
142+
}
143+
}
144+
.Call(Csetlistelt, k, as.integer(j), value)
145+
} else if (is.environment(k) && exists(as.character(name[[3L]]), k, inherits = FALSE)) {
146+
assign(as.character(name[[3L]]), value, k, inherits = FALSE)
147+
} else if (isS4(k)) {
148+
.Call(CsetS4elt, k, as.character(name[[3L]]), value)
149+
}
150+
}
151+
131152
"[.data.table" = function(x, i, j, by, keyby, with=TRUE, nomatch=NA, mult="all", roll=FALSE, rollends=if (roll=="nearest") c(TRUE,TRUE) else if (roll>=0.0) c(FALSE,TRUE) else c(TRUE,FALSE), which=FALSE, .SDcols, verbose=getOption("datatable.verbose"), allow.cartesian=getOption("datatable.allow.cartesian"), drop=NULL, on=NULL, env=NULL, showProgress=getOption("datatable.showProgress", interactive()))
132153
{
133154
# ..selfcount <<- ..selfcount+1 # in dev, we check no self calls, each of which doubles overhead, or could
@@ -1214,21 +1235,8 @@ replace_dot_alias = function(e) {
12141235
setalloccol(x, n, verbose=verbose) # always assigns to calling scope; i.e. this scope
12151236
if (is.name(name)) {
12161237
assign(as.character(name),x,parent.frame(),inherits=TRUE)
1217-
} else if (.is_simple_extraction(name)) { # TODO(#6702): use a helper here as the code is very similar to setDT().
1218-
k = eval(name[[2L]], parent.frame(), parent.frame())
1219-
if (is.list(k)) {
1220-
origj = j = if (name[[1L]] == "$") as.character(name[[3L]]) else eval(name[[3L]], parent.frame(), parent.frame())
1221-
if (is.character(j)) {
1222-
if (length(j)!=1L) stopf("Cannot assign to an under-allocated recursively indexed list -- L[[i]][,:=] syntax is only valid when i is length 1, but its length is %d", length(j))
1223-
j = match(j, names(k))
1224-
if (is.na(j)) internal_error("item '%s' not found in names of list", origj) # nocov
1225-
}
1226-
.Call(Csetlistelt,k,as.integer(j), x)
1227-
} else if (is.environment(k) && exists(as.character(name[[3L]]), k)) {
1228-
assign(as.character(name[[3L]]), x, k, inherits=FALSE)
1229-
} else if (isS4(k)) {
1230-
.Call(CsetS4elt, k, as.character(name[[3L]]), x)
1231-
}
1238+
} else if (.is_simple_extraction(name)) {
1239+
.reassign_extracted_table(name, x)
12321240
} # TO DO: else if env$<- or list$<-
12331241
}
12341242
}
@@ -2973,22 +2981,7 @@ setDT = function(x, keep.rownames=FALSE, key=NULL, check.names=FALSE) {
29732981
assign(name, x, parent.frame(), inherits=TRUE)
29742982
} else if (.is_simple_extraction(name)) {
29752983
# common case is call from 'lapply()'
2976-
k = eval(name[[2L]], parent.frame(), parent.frame())
2977-
if (is.list(k)) {
2978-
origj = j = if (name[[1L]] == "$") as.character(name[[3L]]) else eval(name[[3L]], parent.frame(), parent.frame())
2979-
if (length(j) == 1L) {
2980-
if (is.character(j)) {
2981-
j = match(j, names(k))
2982-
if (is.na(j))
2983-
stopf("Item '%s' not found in names of input list", origj)
2984-
}
2985-
}
2986-
.Call(Csetlistelt, k, as.integer(j), x)
2987-
} else if (is.environment(k) && exists(as.character(name[[3L]]), k)) {
2988-
assign(as.character(name[[3L]]), x, k, inherits=FALSE)
2989-
} else if (isS4(k)) {
2990-
.Call(CsetS4elt, k, as.character(name[[3L]]), x)
2991-
}
2984+
.reassign_extracted_table(name, x)
29922985
} else if (name %iscall% "get") { # #6725
29932986
# edit 'get(nm, env)' call to be 'assign(nm, x, envir=env)'
29942987
name = match.call(get, name)

inst/tests/tests.Rraw

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15879,8 +15879,12 @@ test(2074.03, data.table(grade=c(50L, 91L, 95L, 51L, 89L))[ , .N, by=evaluate(gr
1587915879
data.table(evaluate=c('F', 'A', 'B'), N=c(2L, 2L, 1L)))
1588015880
## error: use recursive character list indexing to assign when also doing alloc.col()
1588115881
opt = options(datatable.alloccol=1L)
15882-
l = list(foo = list(bar = data.table(a = 1:3, b = 4:6)))
15883-
test(2074.04, l[[c('foo', 'bar')]][ , (letters) := 16:18], error = 'under-allocated recursively indexed list')
15882+
l = list(foo=list(bar=data.frame(a=1:3, b=4:6)))
15883+
test(2074.041, setDT(l[[c('foo', 'bar')]]), error='Invalid set* operation on a recursive index L[[i]]')
15884+
setDT(l[['foo']][['bar']])
15885+
test(2074.042, is.data.table(l[[c('foo', 'bar')]]))
15886+
l = list(foo=list(bar=data.table(a=1:3, b=4:6)))
15887+
test(2074.043, l[[c('foo', 'bar')]][ , (letters) := 16:18], error='Invalid set* operation on a recursive index L[[i]]')
1588415888
options(opt)
1588515889
## alloc.col when using 0-truelength j assigning to a subset
1588615890
DT = data.table(a=1)

0 commit comments

Comments
 (0)