diff --git a/NEWS.md b/NEWS.md index 4a62f35888..c111497f70 100644 --- a/NEWS.md +++ b/NEWS.md @@ -42,6 +42,8 @@ 10. `subset()` method for data.tables supports `drop = TRUE` for consistency to data.frame, [#7859](https://github.com/Rdatatable/data.table/issues/7859). Thanks @MichaelChirico for the report and fix. +11. `setorderv()` now accepts a named vector for the `order` argument. When provided, the names are used to identify the columns, allowing the `cols` argument to be omitted, [#6932](https://github.com/Rdatatable/data.table/issues/6932). Thanks to @MichaelChirico 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. diff --git a/R/setkey.R b/R/setkey.R index 14aa5b9b85..e0ed09332b 100644 --- a/R/setkey.R +++ b/R/setkey.R @@ -256,6 +256,10 @@ setorder = function(x, ..., na.last=FALSE) setorderv = function(x, cols = colnames(x), order=1L, na.last=FALSE) { + if (missing(cols) && !is.null(names(order))) { + cols = names(order) + if (anyDuplicated(cols)) stopf("order argument has named duplicates: %s", brackify(duplicated_values(cols))) + } if (is.null(cols)) return(x) if (!is.data.frame(x)) stopf("x must be a data.frame or data.table") na.last = as.logical(na.last) diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index ffadaa6c3f..aef0f292bb 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -21939,3 +21939,12 @@ test(2384.3, print(DT, na.print=".", topn=2, col.names="none", row.names=FALSE, output=c(" .\n e\n ---\n w\n .")) DT = data.table(a=c("x\ny","z"), b=1:2) test(2384.4, print(DT, col.names="none", row.names=FALSE, class=FALSE), output=c(" x\\ny 1\n z 2")) + +# #6932 setorderv() could take order= +DT = data.table(a=c(2,1,2), b=c("b","a","b"), c=c(5,10,3)) +m = c(a=1L, b=1L, c=-1L) +test(2385.01, setorderv(copy(DT), order=m), setorderv(copy(DT), cols=names(m), order=m)) +test(2385.02, {DT=data.table(x=c(2,1,2),y=c(2,1,1)); setorderv(DT, order=c(x=1L,y=-1L)); DT}, data.table(x=c(1,2,2),y=c(1,2,1))) +test(2385.03, setorderv(data.table(x=1:3), order=c(y=1L)), error="some columns are not in the data.table") +test(2385.04, setorderv(data.table(x=1:3), order=c(x=1L,x=-1L)), error="order argument has named duplicates") +test(2385.05, setorderv(data.table(x=1:3), order=c(x=2L)), error="Must be +1 or -1") diff --git a/man/setorder.Rd b/man/setorder.Rd index b4a346cf18..2f578f2129 100644 --- a/man/setorder.Rd +++ b/man/setorder.Rd @@ -42,11 +42,13 @@ is missing (ex: \code{setorder(x)}), \code{x} is rearranged based on all columns in ascending order by default. To sort by a column in descending order prefix the symbol \code{"-"} which means "descending" (\emph{not} "negative", in this context), i.e., \code{setorder(x, a, -b, c)}. The \code{-b} works when \code{b} is of type \code{character} as well. } -\item{cols}{ A character vector of column names of \code{x} by which to order. By default, sorts over all columns; \code{cols = NULL} will return \code{x} untouched. Do not add \code{"-"} here. Use \code{order} argument instead. } +\item{cols}{ A character vector of column names of \code{x} by which to order. By default, sorts over all columns; \code{cols = NULL} will return \code{x} untouched. Do not add \code{"-"} here. Use \code{order} argument instead. This argument can be omitted if \code{order} is a named vector. } \item{order}{ An integer vector with only possible values of \code{1} and \code{-1}, corresponding to ascending and descending order. The length of \code{order} must be either \code{1} or equal to that of \code{cols}. If -\code{length(order) == 1}, it is recycled to \code{length(cols)}. } +\code{length(order) == 1}, it is recycled to \code{length(cols)}. \code{order} +can also be a named vector, in which case the names are used as \code{cols} +and the \code{cols} argument can be omitted. } \item{na.last}{ \code{logical}. If \code{TRUE}, missing values in the data are placed last; if \code{FALSE}, they are placed first; if \code{NA} they are removed. \code{na.last=NA} is valid only for \code{x[order(., na.last)]} and related \code{sort_by(x, .)} (\R \ifelse{html}{\out{≥}}{\eqn{\ge}} 4.4.0) and its default is \code{TRUE}. \code{setorder} and \code{setorderv} only accept @@ -65,7 +67,9 @@ Note that \code{-b} also works with columns of type \code{character} unlike \code{\link[base]{order}}, which requires \code{-xtfrm(y)} instead (which is slow). \code{setorderv} in turn accepts a character vector of column names and an -integer vector of column order separately. +integer vector of column order separately. Additionally, \code{setorderv} can +accept a named integer vector for \code{order}, in which case \code{cols} is +inferred from the names and can be omitted. Note that \code{\link{setkey}} still requires and will always sort only in ascending order, and is different from \code{setorder} in that it additionally @@ -133,6 +137,10 @@ setorder(DT, A, -B) # same as above, but using setorderv setorderv(DT, c("A", "B"), c(1, -1)) + +# infer cols from named order mapping +mapping = c(A = 1L, B = -1L) +setorderv(DT, order = mapping) } \keyword{ data }