From 00c2c67d76980e0f8dcaeb393d83a5b87423d308 Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Fri, 20 Dec 2019 17:34:08 +0800 Subject: [PATCH] rbindlist works for expression column (#3811) --- NEWS.md | 2 ++ inst/tests/tests.Rraw | 11 ++++++++--- src/assign.c | 3 ++- src/rbindlist.c | 7 ++++--- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/NEWS.md b/NEWS.md index 9cb57abd1..d1b6b90bc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -69,6 +69,8 @@ unit = "s") 8. Compiler support for OpenMP is now detected during installation, which allows data.table to compile from source (in single threaded mode) on macOS which, frustratingly, does not include OpenMP support by default, [#2161](https://github.com/Rdatatable/data.table/issues/2161), unlike Windows and Linux. A helpful message is emitted during installation from source, and on package startup as before. Many thanks to @jimhester for the PR. This was typically a problem just after release to CRAN in the few days before macOS binaries (which do support OpenMP) are made available by CRAN. +9. `rbindlist` now supports columns of type `expression`, [#546](https://github.com/Rdatatable/data.table/issues/546). Thanks @jangorecki for the report. + ## BUG FIXES 1. A NULL timezone on POSIXct was interpreted by `as.IDate` and `as.ITime` as UTC rather than the session's default timezone (`tz=""`) , [#4085](https://github.com/Rdatatable/data.table/issues/4085). diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index 9f8a48848..7fc969b55 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -16673,7 +16673,12 @@ test(2128.3, DT[, .SD, .SDcols=function(x) x==1], error='conditions were not met test(2128.4, DT[, .SD, .SDcols=function(x) 2L], error='conditions were not met for: [a, b, c]') test(2128.5, DT[, .SD, .SDcols=function(x) NA], error='conditions were not met for: [a, b, c]') +# expression columns in rbindlist, #546 +A = data.table(c1 = 1, c2 = 'asd', c3 = expression(as.character(Sys.time()))) +B = data.table(c1 = 3, c2 = 'qwe', c3 = expression(as.character(Sys.time()+5))) +test(2129, rbind(A,B)$c3, expression(as.character(Sys.time()), as.character(Sys.time()+5))) -################################### -# Add new tests above this line # -################################### + +######################## +# Add new tests here # +######################## diff --git a/src/assign.c b/src/assign.c index f09d90411..d8526550c 100644 --- a/src/assign.c +++ b/src/assign.c @@ -1061,7 +1061,8 @@ const char *memrecycle(SEXP target, SEXP where, int start, int len, SEXP source, BODY(SEXP, STRING_PTR, SEXP, val, SET_STRING_ELT(target, off+i, cval)) } case VECSXP : - if (TYPEOF(source)!=VECSXP) + case EXPRSXP : // #546 + if (TYPEOF(source)!=VECSXP && TYPEOF(source)!=EXPRSXP) BODY(SEXP, &, SEXP, val, SET_VECTOR_ELT(target, off+i, cval)) else BODY(SEXP, VECTOR_PTR, SEXP, val, SET_VECTOR_ELT(target, off+i, cval)) diff --git a/src/rbindlist.c b/src/rbindlist.c index d85c9d916..fe997b6da 100644 --- a/src/rbindlist.c +++ b/src/rbindlist.c @@ -292,7 +292,8 @@ SEXP rbindlist(SEXP l, SEXP usenamesArg, SEXP fillArg, SEXP idcolArg) } SEXP thisCol = VECTOR_ELT(li, w); int thisType = TYPEOF(thisCol); - if (TYPEORDER(thisType)>TYPEORDER(maxType)) maxType=thisType; + // Use >= for #546 -- TYPEORDER=0 for both LGLSXP and EXPRSXP (but also NULL) + if (TYPEORDER(thisType)>=TYPEORDER(maxType) && !isNull(thisCol)) maxType=thisType; if (isFactor(thisCol)) { if (isNull(getAttrib(thisCol,R_LevelsSymbol))) error("Column %d of item %d has type 'factor' but has no levels; i.e. malformed.", w+1, i+1); factor = true; @@ -513,9 +514,9 @@ SEXP rbindlist(SEXP l, SEXP usenamesArg, SEXP fillArg, SEXP idcolArg) if (w==-1 || !length(thisCol=VECTOR_ELT(li, w))) { // !length for zeroCol warning above; #1871 writeNA(target, ansloc, thisnrow); // writeNA is integer64 aware and writes INT64_MIN } else { - if (TYPEOF(target)==VECSXP && TYPEOF(thisCol)!=VECSXP) { + if ((TYPEOF(target)==VECSXP || TYPEOF(target)==EXPRSXP) && TYPEOF(thisCol)!=TYPEOF(target)) { // do an as.list() on the atomic column; #3528 - thisCol = PROTECT(coerceVector(thisCol, VECSXP)); nprotect++; + thisCol = PROTECT(coerceVector(thisCol, TYPEOF(target))); nprotect++; } // else coerces if needed within memrecycle; with a no-alloc direct coerce from 1.12.4 (PR #3909) const char *ret = memrecycle(target, R_NilValue, ansloc, thisnrow, thisCol, idcol+j+1, foundName);