Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@

7. Rows can now be deleted by reference using `DT[i, .ROW := NULL]`, avoiding a full copy of the table for large row-removal operations, [#635](https://github.com/Rdatatable/data.table/issues/635). This has been one of data.table's most requested features. Target rows must be selected with the `i` expression, `by`/`keyby` are not supported, and keys/indices are cleared after deletion. The new experimental helper `setallocrow()` prepares columns for by-reference row operations. Thanks @arunsrinivasan for the feature request, @ben-schwen for the implementation, and @aitap for review and assistance.

8. `tstrsplit()` gains a `rev` argument to facilitate extracting elements anchored from the end of the string, [#6341](https://github.com/Rdatatable/data.table/issues/6341). This is especially useful when strings have a varying number of components and you only want to extract the last or second-to-last element. Thanks to @JBrownArcGen for the suggestion and @venom1204 for the implementation.

### BUG FIXES

1. `fread()` with `skip=0` and `(header=TRUE|FALSE)` no longer skips the first row when it has fewer fields than subsequent rows, [#7463](https://github.com/Rdatatable/data.table/issues/7463). Thanks @emayerhofer for the report and @ben-schwen for the fix.
Expand Down
9 changes: 7 additions & 2 deletions R/transpose.R
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,15 @@ transpose = function(l, fill=NA, ignore.empty=FALSE, keep.names=NULL, make.names
ans[]
}

tstrsplit = function(x, ..., fill=NA, type.convert=FALSE, keep, names=FALSE) {
tstrsplit = function(x, ..., fill=NA, type.convert=FALSE, keep, names=FALSE, rev=FALSE) {
if (!isTRUEorFALSE(names) && !is.character(names))
stopf("'names' must be TRUE/FALSE or a character vector.")
ans = transpose(strsplit(as.character(x), ...), fill=fill, ignore.empty=FALSE)
if (!isTRUEorFALSE(rev))
stopf("'rev' must be TRUE or FALSE.")
ans = strsplit(as.character(x), ...)
if (rev) ans = lapply(ans, base::rev)
ans = transpose(ans, fill=fill, ignore.empty=FALSE)

if (!missing(keep)) {
keep = suppressWarnings(as.integer(keep))
chk = min(keep) >= min(1L, length(ans)) & max(keep) <= length(ans)
Expand Down
5 changes: 5 additions & 0 deletions inst/tests/tests.Rraw
Original file line number Diff line number Diff line change
Expand Up @@ -21850,3 +21850,8 @@ a = c(a=1L)
test(2379.12, as.IDate(d) + 1L, as.IDate(as.Date(d) + 1L))
test(2379.13, as.IDate("2020-01-01") + a, as.IDate(as.Date("2020-01-01") + a))
test(2379.14, as.IDate(d) - 1L, as.IDate(as.Date(d) - 1L))

# #6341: tstrsplit() supports reverse indexing
test(2380.01, tstrsplit(c("ABC-DEF", "ABC-DEF-GHI", "ABC-DEF-GHI-JKL", "ABC-DEF-GHI-JKL-MNO"), "-", fixed=TRUE, keep=1:3, rev=TRUE), list(c("DEF", "GHI", "JKL", "MNO"), c("ABC", "DEF", "GHI", "JKL"), c(NA_character_, "ABC", "DEF", "GHI")))
test(2380.02, tstrsplit(c("A-B", "A-B-C"), "-", fixed=TRUE, rev=TRUE), list(c("B", "C"), c("A", "B"), c(NA_character_, "A")))
test(2380.03, tstrsplit("A-B", "-", rev=NA), error="'rev' must be TRUE or FALSE.")
10 changes: 8 additions & 2 deletions man/tstrsplit.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
}

\usage{
tstrsplit(x, \dots, fill=NA, type.convert=FALSE, keep, names=FALSE)
tstrsplit(x, \dots, fill=NA, type.convert=FALSE, keep, names=FALSE, rev=FALSE)
}
\arguments{
\item{x}{The vector to split (and transpose).}
Expand All @@ -17,9 +17,10 @@ tstrsplit(x, \dots, fill=NA, type.convert=FALSE, keep, names=FALSE)
\item{type.convert}{\code{TRUE} calls \code{\link{type.convert}} with \code{as.is=TRUE} on the columns. May also be a function, list of functions, or named list of functions to apply to each part; see examples. }
\item{keep}{Specify indices corresponding to just those list elements to retain in the transposed result. Default is to return all.}
\item{names}{\code{TRUE} auto names the list with \code{V1, V2} etc. Default (\code{FALSE}) is to return an unnamed list.}
\item{rev}{logical. If \code{TRUE}, each split result is reversed before transposing, so \code{keep} indices count from the end of the string. This is useful when strings have varying numbers of components and extraction should be anchored to the end.}
}
\details{
It internally calls \code{strsplit} first, and then \code{\link{transpose}} on the result.
It internally calls \code{strsplit} first, optionally reverses each split result, and then calls \code{\link{transpose}}.

\code{names} argument can be used to return an auto named list, although this argument does not have any effect when used with \code{:=}, which requires names to be provided explicitly. It might be useful in other scenarios.
}
Expand Down Expand Up @@ -62,6 +63,11 @@ DT[, tstrsplit(z, "/", type.convert=list(as.factor=1L, as.numeric))]

# convert the remaining using 'type.convert(x, as.is=TRUE)' (i.e. what type.convert=TRUE does)
DT[, tstrsplit(v, " ", type.convert=list(as.IDate=4L, function(x) type.convert(x, as.is=TRUE)))]

# using rev to anchor from the end of the string
x = c("ABC-DEF", "ABC-DEF-GHI", "ABC-DEF-GHI-JKL")
tstrsplit(x, "-", fixed=TRUE, keep=1:2, rev=TRUE) # returns the last two elements

}
\seealso{
\code{\link{data.table}}, \code{\link{transpose}}, \code{\link[utils]{type.convert}}
Expand Down
Loading