Skip to content

Commit 09c7518

Browse files
Retain with=FALSE behavior in more j=call:call cases like 1:ncol(DT) (#6487)
* mostly done, two tests failing... * passing tests * tidy up a bit * ws alignment
1 parent 619ca73 commit 09c7518

File tree

3 files changed

+22
-9
lines changed

3 files changed

+22
-9
lines changed

NEWS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ rowwiseDT(
5555
# [1] "V1" "b" "c"
5656
```
5757

58-
5. Queries like `DT[, min(x):max(x)]` now work as expected, i.e. the same as `DT[, seq(min(x), max(x))]` or `with(DT, min(x):max(x))`, [#2069](https://github.com/Rdatatable/data.table/issues/2069). Shorthand like `DT[, a:b]` meaning "select from columns `a` through `b`" still works. Thanks to @franknarf1 for reporting and @jangorecki for the fix.
58+
5. Queries like `DT[, min(x):max(x)]` now work as expected, i.e. the same as `DT[, seq(min(x), max(x))]` or `with(DT, min(x):max(x))`, [#2069](https://github.com/Rdatatable/data.table/issues/2069). Shorthand like `DT[, a:b]` meaning "select from columns `a` through `b`" still works. Thanks to @franknarf1 for reporting, @jangorecki for the fix, and @MichaelChirico for a follow-up ensuring back-compatibility.
5959

6060
6. Fixed a segfault in `fcase()`, [#6448](https://github.com/Rdatatable/data.table/issues/6448). Thanks @ethanbsmith for reporting with reprex, @aitap for finding the root cause, and @MichaelChirico for the PR.
6161

R/data.table.R

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -244,9 +244,11 @@ replace_dot_alias = function(e) {
244244
if (!missing(j)) {
245245
jsub = replace_dot_alias(jsub)
246246
root = root_name(jsub)
247-
if ((root == ":" && !is.call(jsub[[2L]]) && !is.call(jsub[[3L]])) || ## x[, V1:V2]; but not x[, (V1):(V2)] #2069
247+
av = all.vars(jsub)
248+
all..names = FALSE
249+
if ((.is_withFALSE_range(jsub, x, root, av)) ||
248250
(root %chin% c("-","!") && jsub[[2L]] %iscall% '(' && jsub[[2L]][[2L]] %iscall% ':') || ## x[, !(V8:V10)]
249-
( (!length(av<-all.vars(jsub)) || all(startsWith(av, ".."))) && ## x[, "V1"]; x[, ..v]
251+
( (!length(av) || (all..names <- all(startsWith(av, "..")))) && ## x[, "V1"]; x[, ..v]
250252
root %chin% c("","c","paste","paste0","-","!") && ## x[, c("V1","V2")]; x[, paste("V",1:2,sep="")]; x[, paste0("V",1:2)]
251253
missingby )) { # test 763. TODO: likely that !missingby iff with==TRUE (so, with can be removed)
252254
# When no variable names (i.e. symbols) occur in j, scope doesn't matter because there are no symbols to find.
@@ -261,18 +263,19 @@ replace_dot_alias = function(e) {
261263
# want decisions like this to depend on the data or vector lengths since that can introduce
262264
# inconsistency reminiscent of drop=TRUE in [.data.frame that we seek to avoid.
263265
with=FALSE
264-
if (length(av)) {
266+
if (all..names) {
265267
for (..name in av) {
266268
name = substr(..name, 3L, nchar(..name))
267269
if (!nzchar(name)) stopf("The symbol .. is invalid. The .. prefix must be followed by at least one character.")
270+
parent_has_..name = exists(..name, where=parent.frame())
268271
if (!exists(name, where=parent.frame())) {
269-
suggested = if (exists(..name, where=parent.frame()))
272+
suggested = if (parent_has_..name)
270273
gettextf(" Variable '..%s' does exist in calling scope though, so please just removed the .. prefix from that variable name in calling scope.", name)
271274
# We have recommended 'manual' .. prefix in the past, so try to be helpful
272275
else
273276
""
274277
stopf("Variable '%s' is not found in calling scope. Looking in calling scope because you used the .. prefix.%s", name, suggested)
275-
} else if (exists(..name, where=parent.frame())) {
278+
} else if (parent_has_..name) {
276279
warningf("Both '%1$s' and '..%1$s' exist in calling scope. Please remove the '..%1$s' variable in calling scope for clarity.", name)
277280
}
278281
}
@@ -3028,6 +3031,13 @@ rleidv = function(x, cols=seq_along(x), prefix=NULL) {
30283031
ids
30293032
}
30303033

3034+
.is_withFALSE_range = function(e, x, root=root_name(e), vars=all.vars(e)) {
3035+
if (root != ":") return(FALSE)
3036+
if (!length(vars)) return(TRUE) # e.g. 1:10
3037+
if (!all(vars %chin% names(x))) return(TRUE) # e.g. 1:ncol(x)
3038+
is.name(e[[1L]]) && is.name(e[[2L]]) # e.g. V1:V2, but not min(V1):max(V2) or 1:max(V2)
3039+
}
3040+
30313041
# GForce functions
30323042
# to add a new function to GForce (from the R side -- the easy part!):
30333043
# (1) add it to gfuns

inst/tests/tests.Rraw

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19165,14 +19165,17 @@ for (opt in c(0, 1, 2)) {
1916519165
}
1916619166

1916719167
# Confusing behavior with DT[, min(var):max(var)] #2069
19168-
DT = data.table(t = c(2L, 1L, 3L))
19168+
DT = data.table(t = c(2L, 1L, 3L), a=0, b=1)
1916919169
test(2284.1, DT[, min(t):max(t)], with(DT, min(t):max(t)))
1917019170
test(2284.2, DT[, 1:max(t)], with(DT, 1:max(t)))
1917119171
test(2284.3, DT[, max(t):1], with(DT, max(t):1))
1917219172
test(2284.4, DT[, 1:t[1L]], with(DT, 1:t[1L]))
1917319173
test(2284.5, DT[, t[2L]:1], with(DT, t[2L]:1))
19174-
test(2284.6, DT[1L, t:max(t)], with(DT[1L], t:max(t)))
19175-
test(2284.7, DT[1L, min(t):t], with(DT[1L], min(t):t))
19174+
test(2284.6, DT[, min(a):max(t)], with(DT, min(a):max(t)))
19175+
# but not every `:` with a call in from or to is with=TRUE, #6486
19176+
test(2284.7, DT[, 1:ncol(DT)], DT)
19177+
test(2284.8, DT[, 2:(ncol(DT) - 1L)], DT[, "a"])
19178+
test(2284.9, DT[, (ncol(DT) - 1L):ncol(DT)], DT[, c("a", "b")])
1917619179

1917719180
# Extra regression tests for #4772, which was already incidentally fixed by #5183.
1917819181
x = data.table(a=1:3)

0 commit comments

Comments
 (0)