Skip to content

Commit fd2d84a

Browse files
rowwise(): Detect and reject non-atomic, non-list column values with clear error message (#7250)
* added check * .. * removed empty lines * vapply_1b * type-> class * lintr + error * updated things * updated news * Update R/rowwiseDT.R --------- Co-authored-by: Benjamin Schwendinger <[email protected]>
1 parent 0912a66 commit fd2d84a

File tree

3 files changed

+24
-1
lines changed

3 files changed

+24
-1
lines changed

NEWS.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828

2929
4. `sum(<int64 column>)` by group is correct with missing entries and GForce activated ([#7571](https://github.com/Rdatatable/data.table/issues/7571)). Thanks to @rweberc for the report and @manmita for the fix. The issue was caused by a faulty early `break` that spilled between groups, and resulted in silently incorrect results!
3030

31-
5. `fread(text=)` could segfault when reading text input ending with a `\x1a` (ASCII SUB) character after a long line, [#7407](https://github.com/Rdatatable/data.table/issues/7407) which is solved by adding check for eof. Thanks @aitap for the report and @manmita for the fix.
31+
5. `fread(text=)` could segfault when reading text input ending with a `\x1a` (ASCII SUB) character after a long line, [#7407](https://github.com/Rdatatable/data.table/issues/7407) which is solved by adding check for eof. Thanks @aitap for the report and @manmita for the fix.
32+
33+
6. `rowwiseDT()` now provides a helpful error message when a complex object that is not a list (e.g., a function) is provided as a cell value, instructing the user to wrap it in `list()`, [#7219](https://github.com/Rdatatable/data.table/issues/7219). Thanks @kylebutts for the report and @venom1204 for the fix.
3234

3335
### Notes
3436

R/rowwiseDT.R

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ rowwiseDT = function(...) {
1313
nrows = length(body) %/% ncols
1414
if (length(body) != nrows * ncols)
1515
stopf("There are %d columns but the number of cells is %d, which is not an integer multiple of the columns", ncols, length(body))
16+
is_problematic = vapply_1b(body, function(v) !(is.atomic(v) || is.null(v) || typeof(v) == "list"))
17+
if (any(is_problematic)) {
18+
idx = which(is_problematic)[1L]
19+
col_idx = (idx - 1L) %% ncols + 1L
20+
col_name = header[col_idx]
21+
obj_type = class1(body[[idx]])
22+
stopf("Column '%s' is type '%s'. Non-atomic, non-list objects must be wrapped in list(), e.g., list(f) instead of f", col_name, obj_type)
23+
}
1624
# make all the non-scalar elements to a list
1725
needs_list = lengths(body) != 1L
1826
body[needs_list] = lapply(body[needs_list], list)

inst/tests/tests.Rraw

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22035,3 +22035,16 @@ if (test_bit64) local({
2203522035
# 7407 Test for fread() handling \x1A (ASCII SUB) at end of input
2203622036
txt = paste0("foo\n", strrep("a", 4096 * 100), "\x1A")
2203722037
test(2359.1, nchar(fread(txt)$foo), 409600L)
22038+
22039+
# rowwiseDT() valid and invalid handling of complex objects #7219
22040+
test(2360.1, rowwiseDT(x =, y =, 1, 2, 3, 4), data.table(x = c(1, 3), y = c(2, 4)))
22041+
test(2360.2, rowwiseDT(x =, func =,
22042+
1, list(\(x) x + 1),
22043+
2, list(function(z) z * 2)),
22044+
data.table(x = c(1, 2), func = list(\(x) x + 1, function(z) z * 2)))
22045+
test(2360.3, rowwiseDT(x =, func =, 1, \(x) x + 1),
22046+
error = "Column 'func' is type 'function'. Non-atomic, non-list objects must be wrapped in list\\(\\)")
22047+
test(2360.4, rowwiseDT(x =, expr =, 1, quote(a + b)),
22048+
error = "Column 'expr' is type 'call'. Non-atomic, non-list objects must be wrapped in list\\(\\)")
22049+
test(2360.5, rowwiseDT(x =, plist =, 1, as.pairlist(list(123))),
22050+
error = "Column 'plist' is type 'pairlist'. Non-atomic, non-list objects must be wrapped in list\\(\\)")

0 commit comments

Comments
 (0)