Skip to content

Commit b9436f4

Browse files
revert #6167 (new rules on list(NULL) assignment)
1 parent f72e46b commit b9436f4

File tree

6 files changed

+2
-53
lines changed

6 files changed

+2
-53
lines changed

NEWS.md

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -133,28 +133,7 @@ rowwiseDT(
133133

134134
19. Grouped queries on keyed tables no longer return an incorrectly keyed result if the _ad hoc_ `by=` list has some function call (in particular, a function which happens to return a strictly decreasing function of the keys), e.g. `by=.(a = rev(a))`, [#5583](https://github.com/Rdatatable/data.table/issues/5583). Thanks @AbrJA for the report and @MichaelChirico for the fix.
135135

136-
20. Assigning `list(NULL)` to a list column now replaces the column with `list(NULL)`, instead of deleting the column [#5558](https://github.com/Rdatatable/data.table/issues/5558). This behavior is now consistent with base `data.frame`. Thanks @tdhock for the report and @joshhwuu for the fix. This is due to a fundamental ambiguity from both allowing list columns _and_ making the use of `list()` to wrap `j=` arguments optional. We think that the code behaves as expected in all cases now. See the below for some illustration:
137-
138-
```r
139-
DT = data.table(L=list(1L), i=2L, c='a')
140-
141-
DT[, i := NULL] # delete i
142-
DT[, L := NULL] # delete L
143-
144-
DT[, i := list(NULL)] # overwrite: identical(DT$i, list(NULL))
145-
# ^ ** THIS IS A CHANGE FROM PREVIOUS BEHAVIOR WHICH WOULD DELETE i **
146-
DT[, L := list(NULL)] # assignment: identical(DT$L, list(NULL))
147-
148-
DT[, i := .(3L)] # assignment: identical(DT$i, 3L)
149-
DT[, i := .('a')] # overwrite: identical(DT$i, 'a')
150-
DT[, L := .(list(NULL))] # assignment: identical(DT$L, list(NULL))
151-
152-
DT[, c('L', 'i') := list(NULL, NULL)] # delete L,i
153-
DT[, c('L', 'i') := list(list(NULL), 3L)] # assignment: identical(DT$L, list(NULL)), identical(DT$i, 3L)
154-
DT[, c('L', 'i') := list(NULL, 3L)] # delete L, assign to i
155-
DT[, c('L', 'i') := list(list(NULL), NULL)] # assign to L, delete i
156-
```
157-
21. An integer overflow in `fread()` with lines longer than `2^(31/2)` bytes is prevented, [#6729](https://github.com/Rdatatable/data.table/issues/6729). The typical impact was no worse than a wrong initial allocation size, corrected later. Thanks to @TaikiSan21 for the report and @aitap for the fix.
136+
20. An integer overflow in `fread()` with lines longer than `2^(31/2)` bytes is prevented, [#6729](https://github.com/Rdatatable/data.table/issues/6729). The typical impact was no worse than a wrong initial allocation size, corrected later. Thanks to @TaikiSan21 for the report and @aitap for the fix.
158137

159138
## NOTES
160139

R/data.table.R

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,8 +1145,6 @@ replace_dot_alias = function(e) {
11451145
}
11461146
}
11471147
names(jsub)=""
1148-
# dont wrap the RHS in list if it is a singular NULL and if not creating a new column
1149-
if (length(jsub[-1L]) == 1L && as.character(jsub[-1L]) == 'NULL' && all(lhs %chin% names_x)) jsub[[1L]]=as.name("identity") else jsub[[1L]]=as.name("list")
11501148
}
11511149
av = all.vars(jsub,TRUE)
11521150
if (!is.atomic(lhs)) stopf("LHS of := must be a symbol, or an atomic vector (column names or positions).")

inst/tests/tests.Rraw

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15447,7 +15447,7 @@ L = list(1:3, NULL, 4:6)
1544715447
test(2058.18, length(L), 3L)
1544815448
test(2058.19, as.data.table(L), data.table(V1=1:3, V2=4:6)) # V2 not V3 # no
1544915449
DT = data.table(a=1:3, b=c(4,5,6))
15450-
test(2058.20, DT[,b:=list(NULL)], data.table(a=1:3, b=list(NULL))) # no
15450+
test(2058.20, DT[,b:=list(NULL)], data.table(a=1:3)) # no
1545115451

1545215452
# rbindlist improved error message, #3638
1545315453
DT = data.table(a=1)

man/assign.Rd

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,6 @@ When \code{LHS} is a factor column and \code{RHS} is a character vector with ite
7878
Unlike \code{<-} for \code{data.frame}, the (potentially large) LHS is not coerced to match the type of the (often small) RHS. Instead the RHS is coerced to match the type of the LHS, if necessary. Where this involves double precision values being coerced to an integer column, a warning is given when fractional data is truncated. It is best to get the column types correct up front and stick to them. Changing a column type is possible but deliberately harder: provide a whole column as the RHS. This RHS is then \emph{plonked} into that column slot and we call this \emph{plonk syntax}, or \emph{replace column syntax} if you prefer. By needing to construct a full length vector of a new type, you as the user are more aware of what is happening and it is clearer to readers of your code that you really do intend to change the column type; e.g., \code{DT[, colA:=as.integer(colA)]}. A plonk occurs whenever you provide a RHS value to `:=` which is \code{nrow} long. When a column is \emph{plonked}, the original column is not updated by reference because that would entail updating every single element of that column whereas the plonk is just one column pointer update.
7979
8080
\code{data.table}s are \emph{not} copied-on-change by \code{:=}, \code{setkey} or any of the other \code{set*} functions. See \code{\link{copy}}.
81-
82-
While in most cases standard and functional form of \code{:=} are interchangeable, there are some minor differences in the way that \code{RHS} is handled. In the functional form, \code{:=} operator behaves like an alias to \code{list}. This means that when \code{RHS} is a list, \code{LHS} is assigned a list. Avoid this by using the standard form when \code{RHS} is a list, or use a vector. See \href{../doc/datatable-reference-semantics.html}{\code{vignette("datatable-reference-semantics")}} for examples.
8381
}
8482
8583
\section{Advanced (internals):}{It is easy to see how \emph{sub-assigning} to existing columns is done internally. Removing columns by reference is also straightforward by modifying the vector of column pointers only (using memmove in C). However adding (new) columns is more tricky as to how the \code{data.table} can be grown \emph{by reference}: the list vector of column pointers is \emph{over-allocated}, see \code{\link{truelength}}. By defining \code{:=} in \code{j} we believe update syntax is natural, and scales, but it also bypasses \code{[<-} dispatch and allows \code{:=} to update by reference with no copies of any part of memory at all.

src/assign.c

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -549,11 +549,6 @@ SEXP assign(SEXP dt, SEXP rows, SEXP cols, SEXP newcolnames, SEXP values)
549549
for (int i=0; i<length(cols); ++i) {
550550
coln = INTEGER(cols)[i]-1;
551551
SEXP thisvalue = RHS_list_of_columns ? VECTOR_ELT(values, i) : values;
552-
// if values is list(NULL), then replace with a list of NULLs instead of deleting, #5558
553-
if (RHS_list_of_columns && length(values)==1 && TYPEOF(VECTOR_ELT(values, 0))==NILSXP && coln < oldncol) {
554-
SET_VECTOR_ELT(dt, coln, targetcol=allocNAVector(VECSXP, length(VECTOR_ELT(dt, coln))));
555-
continue;
556-
}
557552
if (TYPEOF(thisvalue)==NILSXP) {
558553
if (!isNull(rows)) internal_error(__func__, "earlier error 'When deleting columns, i should not be provided' did not happen"); // # nocov
559554
ndelete++;

vignettes/datatable-reference-semantics.Rmd

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -118,33 +118,12 @@ Note that the code above explains how `:=` can be used. They are not working exa
118118

119119
* On the other hand, (b) is handy if you would like to jot some comments down for later.
120120

121-
* In general, (a) and (b) should produce the same result, however there are minor differences in certain cases due to the implementation details of `:=`. Specifically, when using (b), the `:=` operator behaves like an alias to `list`, therefore if `RHS` is a `list` in functional form the column will become a list. See example below.
122-
123121
* The result is returned *invisibly*.
124122

125123
* Since `:=` is available in `j`, we can combine it with `i` and `by` operations just like the aggregation operations we saw in the previous vignette.
126124

127125
#
128126

129-
Although in most cases they are the same, there is a minor difference between the standard and functional forms mentioned above. Let's see an example to understand this.
130-
```{r}
131-
DT = data.table(a = list('A', 'B', 'C'))
132-
l = list(1L:3L)
133-
134-
DT[, new_int := l] # Standard form, new column is integer
135-
136-
DT[, `:=`(new_list = l)] # Functional form, new column is a list of integer vectors
137-
138-
DT[, new_list := list(l)] # Same as above, we can see that `:=` is an alias to list in functional form
139-
140-
# This can be avoided by using a vector instead of a list:
141-
v = 1L:3L
142-
143-
DT[, new_int := v] # Standard form, new column is integer
144-
145-
DT[, `:=`(new_int = v)] # Functional form, new column is integer
146-
```
147-
148127
In the two forms of `:=` shown above, note that we don't assign the result back to a variable. Because we don't need to. The input *data.table* is modified by reference. Let's go through examples to understand what we mean by this.
149128

150129
For the rest of the vignette, we will work with `flights` *data.table*.

0 commit comments

Comments
 (0)