diff --git a/NAMESPACE b/NAMESPACE index 361b706d3..9d5ff618c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -75,7 +75,7 @@ export(fctr) S3method("[", data.table) S3method("[<-", data.table) # S3method("[[", data.table) -# S3method("[[<-", data.table) +S3method("[[<-", data.table) S3method("$<-", data.table) S3method(print, data.table) S3method(as.data.table, data.table) diff --git a/NEWS.md b/NEWS.md index 4a821991b..df2437cd6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -346,6 +346,8 @@ See [#2611](https://github.com/Rdatatable/data.table/issues/2611) for details. T 23. `fread()` auto-detects separators for single-column files consisting solely of quoted values (e.g. `"this_that"\n"2025-01-01 00:00:01"`), [#7366](https://github.com/Rdatatable/data.table/issues/7366). Thanks @arunsrinivasan for the report and @ben-schwen for the fix. +24. Assigning via `[[<-` now changes data.tables by reference, so forms like `DT[["col"]] = value` or `DT[[1, "col"]] = value` keep over-allocation intact instead of falling back to the data.frame method, [#6734](https://github.com/Rdatatable/data.table/issues/6734). Thanks @mb706 for the report and @ben-schwen for the implementation. + ### NOTES 1. The following in-progress deprecations have proceeded: diff --git a/R/as.data.table.R b/R/as.data.table.R index f849aa8c1..677e72189 100644 --- a/R/as.data.table.R +++ b/R/as.data.table.R @@ -141,6 +141,12 @@ as.data.table.list = function(x, rownames_ = NULL check_rownames = !isFALSE(keep.rownames) + # conversion must happen after capturing names + if (is.data.table(x)) { + # operate on plain list to avoid [[<-.data.table dispatch + x = as.list(x) + } + for (i in seq_len(n)) { xi = x[[i]] if (is.null(xi)) next # eachncol already initialized to 0 by integer() above diff --git a/R/data.table.R b/R/data.table.R index db74384c4..9e6735782 100644 --- a/R/data.table.R +++ b/R/data.table.R @@ -2308,6 +2308,23 @@ tail.data.table = function(x, n=6L, ...) { set(x,j=name,value=value) # important i is missing here } +"[[<-.data.table" = function(x, i, j, value) { + n = nargs() + if (!cedta()) { + # nocov start + ans = if (n<4L) `[<-.data.frame`(x, i, value=value) + else `[<-.data.frame`(x, i, j, value) + return(setalloccol(ans)) # over-allocate (again) + # nocov end + } + x = copy(x) + if (n<4L) { + set(x, j=i, value=value) + } else { + set(x, i, j, value) + } +} + as.data.frame.data.table = function(x, row.names = NULL, ...) { ans = setDF(copy(x), rownames = row.names) # issue #5319 diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index e6ef9be1d..c6d622797 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -1405,10 +1405,18 @@ test(437.2, truelength(DT), 2044L) DT = data.table(a=1:3,b=4:6) tl = truelength(DT) DT$foo = 7L -test(438, truelength(DT), tl) +test(438.1, truelength(DT), tl) DT[,"bar"] = 8L -test(439, truelength(DT), tl+2L) -test(440, DT, data.table(a=1:3,b=4:6,foo=7L,bar=8L)) +test(438.2, truelength(DT), tl+2L) +# also allow DT[[...]]= syntax #6734 +DT[["baz"]] = 1L +test(438.3, truelength(DT), tl+2L) +test(438.4, DT$baz, c(1L,1L,1L)) +DT[[5]] = 9L +test(438.5, truelength(DT), tl+3L) +DT[[1,"qux"]] = 10L +test(438.6, truelength(DT), tl+3L) +test(438.7, DT, data.table(a=1:3,b=4:6,foo=7L,bar=8L,baz=9L,qux=c(10L,NA_integer_,NA_integer_))) # Test rbind works by colname now, for consistency with base, FR#1634 DT = data.table(a=1:3,b=4:6) @@ -4089,6 +4097,8 @@ test(1140, as.data.table(X), data.table(a=c(1:2,1L), b=c(1:3)), test(1141.1, data.table(a=1:2, b=1:3), data.table(a=c(1L,2L,1L), b=1:3), warning="Item 1 has 2 rows but longest item has 3; recycled") test(1141.2, data.table(a=1:2, data.table(x=1:5, y=6:10)), data.table(a=c(1L,2L,1L,2L,1L), x=1:5, y=6:10), warning="Item 1 has 2 rows but longest item has 5; recycled") test(1141.3, data.table(a=1:5, data.table(x=c(1,2), y=c(3,4))), data.table(a=c(1:5), x=c(1,2,1,2,1), y=c(3,4,3,4,3)), warning="Item 2 has 2 rows but longest item has 5; recycled") +x = structure(list(1, 2), class = c("list", "data.table")) +test(1141.4, as.data.table(x), data.table(V1=c(1), V2=c(2))) # Fix for bug #79 - DT[, foo()] returns function definition. DT <- data.table(a=1:2)