From aaa09aa8cba5b167f888b977abc1e802b5447734 Mon Sep 17 00:00:00 2001 From: venom1204 Date: Tue, 12 Aug 2025 21:14:09 +0000 Subject: [PATCH 1/6] added check --- R/rowwiseDT.R | 21 +++++++++++++++++++++ inst/tests/tests.Rraw | 12 ++++++++++++ 2 files changed, 33 insertions(+) diff --git a/R/rowwiseDT.R b/R/rowwiseDT.R index 81114ead63..a399138f4a 100644 --- a/R/rowwiseDT.R +++ b/R/rowwiseDT.R @@ -1,3 +1,6 @@ +# This function assumes 'stopf' is available, as it is inside the data.table package. +# If running standalone, replace `stopf(...)` with `stop(sprintf(...))`. + rowwiseDT = function(...) { x = substitute(list(...))[-1L] if (is.null(nms <- names(x))) @@ -13,6 +16,24 @@ rowwiseDT = function(...) { nrows = length(body) %/% ncols if (length(body) != nrows * ncols) stopf("There are %d columns but the number of cells is %d, which is not an integer multiple of the columns", ncols, length(body)) + + is_problematic = vapply( + body, + function(v) !is.atomic(v) && !is.null(v) && typeof(v) != "list", + FUN.VALUE = logical(1L) + ) + + if (any(is_problematic)) { + first_problem_idx = which(is_problematic)[1L] + col_idx = (first_problem_idx - 1L) %% ncols + 1L + col_name = header[col_idx] + obj_type = typeof(body[[first_problem_idx]]) + stopf( + "In column '%s', received an object of type '%s'.\nComplex objects (like functions, models, etc.) must be wrapped in list() to be stored in a data.table column.\nPlease use `list(...)` for this value.", + col_name, + obj_type + ) + } # make all the non-scalar elements to a list needs_list = lengths(body) != 1L body[needs_list] = lapply(body[needs_list], list) diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index d7ffc476bb..b84194cc91 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -21620,3 +21620,15 @@ local({ test(2338.9, {fwrite(dd, f, forceDecimal=FALSE); fread(f)}, di) }) +# rowwiseDT() valid and invalid handling of complex objects #7219 +test(2339.1, rowwiseDT(x =, y =, 1, 2, 3, 4), data.table(x = c(1, 3), y = c(2, 4))) +test(2339.2, rowwiseDT(x =, func =, + 1, list(\(x) x + 1), + 2, list(function(z) z * 2)), + data.table(x = c(1, 2), func = list(\(x) x + 1, function(z) z * 2))) +test(2339.3, rowwiseDT(x =, func =, 1, \(x) x + 1), + error = "In column 'func', received an object of type 'closure'.*wrap.*list") +test(2339.4, rowwiseDT(x =, expr =, 1, quote(a + b)), + error = "In column 'expr', received an object of type 'language'.*wrap.*list") +test(2339.5, rowwiseDT(x =, plist =, 1, as.pairlist(list(123))), + error = "In column 'plist', received an object of type 'pairlist'.*wrap.*list") From 2110d98e4ef8da9a50c3489d840e3a6ad0744fe9 Mon Sep 17 00:00:00 2001 From: venom1204 Date: Tue, 12 Aug 2025 21:20:12 +0000 Subject: [PATCH 2/6] .. --- R/rowwiseDT.R | 3 --- 1 file changed, 3 deletions(-) diff --git a/R/rowwiseDT.R b/R/rowwiseDT.R index a399138f4a..d177e828e7 100644 --- a/R/rowwiseDT.R +++ b/R/rowwiseDT.R @@ -1,6 +1,3 @@ -# This function assumes 'stopf' is available, as it is inside the data.table package. -# If running standalone, replace `stopf(...)` with `stop(sprintf(...))`. - rowwiseDT = function(...) { x = substitute(list(...))[-1L] if (is.null(nms <- names(x))) From 2f777d54166fad12bb40012a3f55e5f1fa6fbaf4 Mon Sep 17 00:00:00 2001 From: venom1204 Date: Fri, 15 Aug 2025 07:04:49 +0000 Subject: [PATCH 3/6] removed empty lines --- R/rowwiseDT.R | 2 -- 1 file changed, 2 deletions(-) diff --git a/R/rowwiseDT.R b/R/rowwiseDT.R index d177e828e7..b5d3d4d7cf 100644 --- a/R/rowwiseDT.R +++ b/R/rowwiseDT.R @@ -13,13 +13,11 @@ rowwiseDT = function(...) { nrows = length(body) %/% ncols if (length(body) != nrows * ncols) stopf("There are %d columns but the number of cells is %d, which is not an integer multiple of the columns", ncols, length(body)) - is_problematic = vapply( body, function(v) !is.atomic(v) && !is.null(v) && typeof(v) != "list", FUN.VALUE = logical(1L) ) - if (any(is_problematic)) { first_problem_idx = which(is_problematic)[1L] col_idx = (first_problem_idx - 1L) %% ncols + 1L From 669f6ad6d4369cfda5e1ef6dff853fc56b476171 Mon Sep 17 00:00:00 2001 From: venom1204 Date: Mon, 25 Aug 2025 09:48:50 +0000 Subject: [PATCH 4/6] vapply_1b --- R/rowwiseDT.R | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/R/rowwiseDT.R b/R/rowwiseDT.R index b5d3d4d7cf..20c038b7f0 100644 --- a/R/rowwiseDT.R +++ b/R/rowwiseDT.R @@ -13,10 +13,9 @@ rowwiseDT = function(...) { nrows = length(body) %/% ncols if (length(body) != nrows * ncols) stopf("There are %d columns but the number of cells is %d, which is not an integer multiple of the columns", ncols, length(body)) - is_problematic = vapply( + is_problematic = vapply_1b( body, - function(v) !is.atomic(v) && !is.null(v) && typeof(v) != "list", - FUN.VALUE = logical(1L) + function(v) !(is.atomic(v) || is.null(v) || typeof(v) == "list") ) if (any(is_problematic)) { first_problem_idx = which(is_problematic)[1L] From e2de9c0434f7af52ae0246f361f9a6c64030fb78 Mon Sep 17 00:00:00 2001 From: venom1204 Date: Sat, 30 Aug 2025 10:43:27 +0000 Subject: [PATCH 5/6] type-> class --- NEWS.md | 2 ++ R/rowwiseDT.R | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index fc69449438..c7ec09e31c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -107,6 +107,8 @@ 18. `fwrite` now allows `dec` to be the same as `sep` for edge cases where only one will be written, e.g. 0-row or 1-column tables. [#7227](https://github.com/Rdatatable/data.table/issues/7227). Thanks @MichaelChirico for the report and @venom1204 for the fix. +19. `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. + ### NOTES 1. The following in-progress deprecations have proceeded: diff --git a/R/rowwiseDT.R b/R/rowwiseDT.R index 20c038b7f0..d2fd9723fa 100644 --- a/R/rowwiseDT.R +++ b/R/rowwiseDT.R @@ -21,9 +21,9 @@ rowwiseDT = function(...) { first_problem_idx = which(is_problematic)[1L] col_idx = (first_problem_idx - 1L) %% ncols + 1L col_name = header[col_idx] - obj_type = typeof(body[[first_problem_idx]]) + obj_type = class(body[[first_problem_idx]])[1L] stopf( - "In column '%s', received an object of type '%s'.\nComplex objects (like functions, models, etc.) must be wrapped in list() to be stored in a data.table column.\nPlease use `list(...)` for this value.", + "In column '%s', received an object of type '%s'.\nComplex objects (like functions, formulas, or calls) must be wrapped in list() to be stored in a data.table column.\nPlease use `list(...)` for this value.", col_name, obj_type ) From dafc34ff9df6086b7862d8b7ca22c9f93c890959 Mon Sep 17 00:00:00 2001 From: venom1204 Date: Sat, 30 Aug 2025 11:13:03 +0000 Subject: [PATCH 6/6] lintr + error --- R/rowwiseDT.R | 2 +- inst/tests/tests.Rraw | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/R/rowwiseDT.R b/R/rowwiseDT.R index d2fd9723fa..ad19e6ab54 100644 --- a/R/rowwiseDT.R +++ b/R/rowwiseDT.R @@ -21,7 +21,7 @@ rowwiseDT = function(...) { first_problem_idx = which(is_problematic)[1L] col_idx = (first_problem_idx - 1L) %% ncols + 1L col_name = header[col_idx] - obj_type = class(body[[first_problem_idx]])[1L] + obj_type = class1(body[[first_problem_idx]]) stopf( "In column '%s', received an object of type '%s'.\nComplex objects (like functions, formulas, or calls) must be wrapped in list() to be stored in a data.table column.\nPlease use `list(...)` for this value.", col_name, diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index c1ca6ee220..3245ba9c84 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -21663,8 +21663,8 @@ test(2340.2, rowwiseDT(x =, func =, 2, list(function(z) z * 2)), data.table(x = c(1, 2), func = list(\(x) x + 1, function(z) z * 2))) test(2340.3, rowwiseDT(x =, func =, 1, \(x) x + 1), - error = "In column 'func', received an object of type 'closure'.*wrap.*list") + error = "In column 'func', received an object of type 'function'.*wrap.*list") test(2340.4, rowwiseDT(x =, expr =, 1, quote(a + b)), - error = "In column 'expr', received an object of type 'language'.*wrap.*list") + error = "In column 'expr', received an object of type 'call'.*wrap.*list") test(2340.5, rowwiseDT(x =, plist =, 1, as.pairlist(list(123))), error = "In column 'plist', received an object of type 'pairlist'.*wrap.*list")