From d01879af7007aad0745f1249b91327b8d943ea45 Mon Sep 17 00:00:00 2001 From: asardaes Date: Sun, 23 Jun 2019 23:47:17 +0200 Subject: [PATCH] Add .negate to filter_on (#6) --- NEWS.md | 1 + R/VERBS-filter_on.R | 12 ++++++++++-- inst/NEWS.md | 1 + tests/testthat/unit/test-filter_on.R | 6 ++++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index a6cce24..e32ac12 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,7 @@ - `transmute` is no longer an alias for `select`. This is to make the latter more flexible in some bare selection cases (like combining several `tidyselect` calls), and leave the former more simple and preferable for actual transmutation cases. +- `filter_on` gains a `.negate` parameter. # table.express 0.1.1 diff --git a/R/VERBS-filter_on.R b/R/VERBS-filter_on.R index b042231..90ab9fd 100644 --- a/R/VERBS-filter_on.R +++ b/R/VERBS-filter_on.R @@ -4,11 +4,14 @@ #' #' @export #' @importFrom rlang abort +#' @importFrom rlang expr #' #' @template data-arg #' @param ... Key-value pairs, see details. #' @param nomatch See [data.table::data.table]. #' @param mult See [data.table::data.table]. +#' @param .negate Whether to negate the expression and search only for rows that don't contain the +#' given values. #' @template chain-arg #' #' @details @@ -30,7 +33,7 @@ #' start_expr %>% #' filter_on(cyl = 4, gear = 5) #' -filter_on <- function(.data, ..., nomatch = getOption("datatable.nomatch"), mult = "all", +filter_on <- function(.data, ..., nomatch = getOption("datatable.nomatch"), mult = "all", .negate = FALSE, .chain = getOption("table.express.chain", TRUE)) { key_value <- parse_dots(FALSE, ...) @@ -41,7 +44,12 @@ filter_on <- function(.data, ..., nomatch = getOption("datatable.nomatch"), mult rlang::abort("All arguments in '...' must be named.") } - ans <- where(.data, list(!!!values), .parse = FALSE, .chain = .chain) %>% + clause <- rlang::expr(list(!!!values)) + if (.negate) { + clause <- rlang::expr(`!`(`!!`(clause))) + } + + ans <- .data$set_where(clause, .chain) %>% frame_append(on = !!keys, .parse = FALSE) if (!missing(nomatch)) { diff --git a/inst/NEWS.md b/inst/NEWS.md index b22e0e0..8233f67 100644 --- a/inst/NEWS.md +++ b/inst/NEWS.md @@ -4,3 +4,4 @@ - `transmute` is no longer an alias for `select`. This is to make the latter more flexible in some bare selection cases (like combining several `tidyselect` calls), and leave the former more simple and preferable for actual transmutation cases. +- `filter_on` gains a `.negate` parameter. diff --git a/tests/testthat/unit/test-filter_on.R b/tests/testthat/unit/test-filter_on.R index 0140bd8..8c3507f 100644 --- a/tests/testthat/unit/test-filter_on.R +++ b/tests/testthat/unit/test-filter_on.R @@ -24,6 +24,12 @@ test_that("The filter_on verb works as expected.", { expect_identical(nrow(ans), 0L) }) +test_that("The filter_on semantics can be negated.", { + expected <- DT[!list(0), on = "am"] + ans <- DT %>% start_expr %>% filter_on(am = 0, .negate = TRUE) %>% end_expr + expect_identical(ans, expected) +}) + test_that("The filter_on verb works for several values per key.", { expected <- state[.(c("South", "West"), c("South Atlantic", "Pacific"))]