Skip to content

Commit 87804e5

Browse files
setDT() works on S4 slots
1 parent 70c64ac commit 87804e5

File tree

5 files changed

+33
-8
lines changed

5 files changed

+33
-8
lines changed

R/data.table.R

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,7 +1221,7 @@ replace_dot_alias = function(e) {
12211221
setalloccol(x, n, verbose=verbose) # always assigns to calling scope; i.e. this scope
12221222
if (is.name(name)) {
12231223
assign(as.character(name),x,parent.frame(),inherits=TRUE)
1224-
} else if (name %iscall% c('$', '[[') && is.name(name[[2L]])) {
1224+
} else if (.is_simple_extraction(name)) { # TODO(#6702): use a helper here as the code is very similar to setDT().
12251225
k = eval(name[[2L]], parent.frame(), parent.frame())
12261226
if (is.list(k)) {
12271227
origj = j = if (name[[1L]] == "$") as.character(name[[3L]]) else eval(name[[3L]], parent.frame(), parent.frame())
@@ -1233,6 +1233,8 @@ replace_dot_alias = function(e) {
12331233
.Call(Csetlistelt,k,as.integer(j), x)
12341234
} else if (is.environment(k) && exists(as.character(name[[3L]]), k)) {
12351235
assign(as.character(name[[3L]]), x, k, inherits=FALSE)
1236+
} else if (isS4(k)) {
1237+
.Call(CsetS4elt, k, as.character(name[[3L]]), x)
12361238
}
12371239
} # TO DO: else if env$<- or list$<-
12381240
}
@@ -2967,7 +2969,7 @@ setDT = function(x, keep.rownames=FALSE, key=NULL, check.names=FALSE) {
29672969
if (is.name(name)) {
29682970
name = as.character(name)
29692971
assign(name, x, parent.frame(), inherits=TRUE)
2970-
} else if (name %iscall% c('$', '[[') && is.name(name[[2L]])) {
2972+
} else if (.is_simple_extraction(name)) {
29712973
# common case is call from 'lapply()'
29722974
k = eval(name[[2L]], parent.frame(), parent.frame())
29732975
if (is.list(k)) {
@@ -2979,9 +2981,11 @@ setDT = function(x, keep.rownames=FALSE, key=NULL, check.names=FALSE) {
29792981
stopf("Item '%s' not found in names of input list", origj)
29802982
}
29812983
}
2982-
.Call(Csetlistelt,k,as.integer(j), x)
2984+
.Call(Csetlistelt, k, as.integer(j), x)
29832985
} else if (is.environment(k) && exists(as.character(name[[3L]]), k)) {
29842986
assign(as.character(name[[3L]]), x, k, inherits=FALSE)
2987+
} else if (isS4(k)) {
2988+
.Call(CsetS4elt, k, as.character(name[[3L]]), x)
29852989
}
29862990
}
29872991
.Call(CexpandAltRep, x) # issue#2866 and PR#2882
@@ -3048,6 +3052,9 @@ rleidv = function(x, cols=seq_along(x), prefix=NULL) {
30483052
is.name(e[[1L]]) && is.name(e[[2L]]) # e.g. V1:V2, but not min(V1):max(V2) or 1:max(V2)
30493053
}
30503054

3055+
# for assignments like x[[1]][, a := 2] or setDT(x@DT)
3056+
.is_simple_extraction = function(e) e %iscall% c('$', '@', '[[') && is.name(e[[2L]])
3057+
30513058
# GForce functions
30523059
# to add a new function to GForce (from the R side -- the easy part!):
30533060
# (1) add it to gfuns

inst/tests/S4.Rraw

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,11 @@ DT = data.table(a = rep(1:2, c(1, 100)))
109109
# Set the S4 bit on a simple object
110110
DT[, b := asS4(seq_len(.N))]
111111
test(6, DT[, b, by=a, verbose=TRUE][, isS4(b)], output="dogroups: growing")
112+
113+
# setDT() works for a data.frame slot, #6701
114+
setClass("DataFrame", slots=c(x="data.frame"))
115+
DF = new("DataFrame", x=data.frame(a=1))
116+
setDT(DF@x)
117+
test(7.1, is.data.table(DF@x))
118+
setClass("DataTable", slots=c(x="data.table"))
119+
test(7.2, options=c(datatable.alloccol=0L), new("DataTable", x=data.table(a=1))@x[, b := 2L]$b, 2L)

src/data.table.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ SEXP freadR(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SE
299299
SEXP fwriteR(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP);
300300
SEXP rbindlist(SEXP, SEXP, SEXP, SEXP, SEXP);
301301
SEXP setlistelt(SEXP, SEXP, SEXP);
302+
SEXP setS4elt(SEXP, SEXP, SEXP);
302303
SEXP address(SEXP);
303304
SEXP expandAltRep(SEXP);
304305
SEXP fmelt(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP);

src/init.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ R_CallMethodDef callMethods[] = {
7272
{"Crbindlist", (DL_FUNC) &rbindlist, -1},
7373
{"Cvecseq", (DL_FUNC) &vecseq, -1},
7474
{"Csetlistelt", (DL_FUNC) &setlistelt, -1},
75+
{"CsetS4elt", (DL_FUNC) &setS4elt, -1},
7576
{"Caddress", (DL_FUNC) &address, -1},
7677
{"CexpandAltRep", (DL_FUNC) &expandAltRep, -1},
7778
{"Cfmelt", (DL_FUNC) &fmelt, -1},

src/wrappers.c

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,26 @@ SEXP copy(SEXP x)
5959
return(duplicate(x));
6060
}
6161

62+
// Internal use only. So that := can update elements of a list of data.table, #2204. Just needed to overallocate/grow the VECSXP.
6263
SEXP setlistelt(SEXP l, SEXP i, SEXP value)
6364
{
64-
R_len_t i2;
65-
// Internal use only. So that := can update elements of a list of data.table, #2204. Just needed to overallocate/grow the VECSXP.
66-
if (!isNewList(l)) error(_("First argument to setlistelt must be a list()"));
67-
if (!isInteger(i) || LENGTH(i)!=1) error(_("Second argument to setlistelt must a length 1 integer vector"));
68-
i2 = INTEGER(i)[0];
65+
if (!isNewList(l)) internal_error(__func__, "First argument to setlistelt must be a list()");
66+
if (!isInteger(i) || LENGTH(i)!=1) internal_error(__func__, "Second argument to setlistelt must a length 1 integer vector");
67+
R_len_t i2 = INTEGER(i)[0];
6968
if (LENGTH(l) < i2 || i2<1) error(_("i (%d) is outside the range of items [1,%d]"),i2,LENGTH(l));
7069
SET_VECTOR_ELT(l, i2-1, value);
7170
return(R_NilValue);
7271
}
7372

73+
// Internal use only. So that := can update elements of a slot of data.table, #6701.
74+
SEXP setS4elt(SEXP obj, SEXP name, SEXP value)
75+
{
76+
if (!isS4(obj)) internal_error(__func__, "First argument to setS4elt must be an S4 object");
77+
if (!isString(name) || LENGTH(name)!=1) internal_error(__func__, "Second argument to setS4elt must be a character string");
78+
R_do_slot_assign(obj, name, value);
79+
return(R_NilValue);
80+
}
81+
7482
SEXP address(SEXP x)
7583
{
7684
// A better way than : http://stackoverflow.com/a/10913296/403310

0 commit comments

Comments
 (0)