diff --git a/R/000-wrappers.R b/R/000-wrappers.R index 9436c8f9..f4ccd0d5 100644 --- a/R/000-wrappers.R +++ b/R/000-wrappers.R @@ -660,6 +660,13 @@ class(`PlRDataType`) <- c("PlRDataType__bundle", "savvy_neopolars__sealed") } } +`PlRExpr__meta_selector_xor` <- function(self) { + function(`other`) { + `other` <- .savvy_extract_ptr(`other`, "PlRExpr") + .savvy_wrap_PlRExpr(.Call(savvy_PlRExpr__meta_selector_xor__impl, `self`, `other`)) + } +} + `PlRExpr_abs` <- function(self) { function() { .savvy_wrap_PlRExpr(.Call(savvy_PlRExpr_abs__impl, `self`)) @@ -2907,6 +2914,7 @@ class(`PlRDataType`) <- c("PlRDataType__bundle", "savvy_neopolars__sealed") e$`_meta_selector_add` <- `PlRExpr__meta_selector_add`(ptr) e$`_meta_selector_and` <- `PlRExpr__meta_selector_and`(ptr) e$`_meta_selector_sub` <- `PlRExpr__meta_selector_sub`(ptr) + e$`_meta_selector_xor` <- `PlRExpr__meta_selector_xor`(ptr) e$`abs` <- `PlRExpr_abs`(ptr) e$`add` <- `PlRExpr_add`(ptr) e$`agg_groups` <- `PlRExpr_agg_groups`(ptr) diff --git a/R/expr-meta.R b/R/expr-meta.R index 7c74f01a..ed4c0bf6 100644 --- a/R/expr-meta.R +++ b/R/expr-meta.R @@ -98,6 +98,11 @@ expr_meta__selector_sub <- function(other) { wrap() } +expr_meta__selector_xor <- function(other) { + self$`_rexpr`$`_meta_selector_xor`(other$`_rexpr`) |> + wrap() +} + expr_meta__as_selector <- function() { self$`_rexpr`$`_meta_as_selector`() |> wrap() diff --git a/R/selectors.R b/R/selectors.R index 9bfaf8b7..74e4a2cf 100644 --- a/R/selectors.R +++ b/R/selectors.R @@ -120,6 +120,25 @@ selector__and <- function(other) { } } +selector__xor <- function(other) { + if (is_column(other)) { + colname <- other$meta$output_name() + other <- cs$by_name(colname) + } + if (is_polars_selector(other)) { + wrap_to_selector( + self$meta$`_as_selector`()$meta$`_selector_xor`(other), + name = "xor", + parameters = list( + self = self, + other = other + ) + ) + } else { + self$as_expr()$xor(other) + } +} + selector__as_expr <- function() { self$`_rexpr` |> wrap() diff --git a/src/init.c b/src/init.c index 2fff8e59..edb61641 100644 --- a/src/init.c +++ b/src/init.c @@ -459,6 +459,11 @@ SEXP savvy_PlRExpr__meta_selector_sub__impl(SEXP self__, SEXP c_arg__other) { return handle_result(res); } +SEXP savvy_PlRExpr__meta_selector_xor__impl(SEXP self__, SEXP c_arg__other) { + SEXP res = savvy_PlRExpr__meta_selector_xor__ffi(self__, c_arg__other); + return handle_result(res); +} + SEXP savvy_PlRExpr_abs__impl(SEXP self__) { SEXP res = savvy_PlRExpr_abs__ffi(self__); return handle_result(res); @@ -2636,6 +2641,7 @@ static const R_CallMethodDef CallEntries[] = { {"savvy_PlRExpr__meta_selector_add__impl", (DL_FUNC) &savvy_PlRExpr__meta_selector_add__impl, 2}, {"savvy_PlRExpr__meta_selector_and__impl", (DL_FUNC) &savvy_PlRExpr__meta_selector_and__impl, 2}, {"savvy_PlRExpr__meta_selector_sub__impl", (DL_FUNC) &savvy_PlRExpr__meta_selector_sub__impl, 2}, + {"savvy_PlRExpr__meta_selector_xor__impl", (DL_FUNC) &savvy_PlRExpr__meta_selector_xor__impl, 2}, {"savvy_PlRExpr_abs__impl", (DL_FUNC) &savvy_PlRExpr_abs__impl, 1}, {"savvy_PlRExpr_add__impl", (DL_FUNC) &savvy_PlRExpr_add__impl, 2}, {"savvy_PlRExpr_agg_groups__impl", (DL_FUNC) &savvy_PlRExpr_agg_groups__impl, 1}, diff --git a/src/rust/api.h b/src/rust/api.h index 72606378..bfe5178f 100644 --- a/src/rust/api.h +++ b/src/rust/api.h @@ -93,6 +93,7 @@ SEXP savvy_PlRExpr__meta_as_selector__ffi(SEXP self__); SEXP savvy_PlRExpr__meta_selector_add__ffi(SEXP self__, SEXP c_arg__other); SEXP savvy_PlRExpr__meta_selector_and__ffi(SEXP self__, SEXP c_arg__other); SEXP savvy_PlRExpr__meta_selector_sub__ffi(SEXP self__, SEXP c_arg__other); +SEXP savvy_PlRExpr__meta_selector_xor__ffi(SEXP self__, SEXP c_arg__other); SEXP savvy_PlRExpr_abs__ffi(SEXP self__); SEXP savvy_PlRExpr_add__ffi(SEXP self__, SEXP c_arg__rhs); SEXP savvy_PlRExpr_agg_groups__ffi(SEXP self__); diff --git a/src/rust/src/expr/meta.rs b/src/rust/src/expr/meta.rs index e8e4729b..8e4cd1f2 100644 --- a/src/rust/src/expr/meta.rs +++ b/src/rust/src/expr/meta.rs @@ -91,16 +91,15 @@ impl PlRExpr { Ok(out.into()) } - // TODO: enable after polars update - // fn meta_selector_xor(&self, other: &PlRExpr) -> Result { - // let out = self - // .inner - // .clone() - // .meta() - // ._selector_xor(other.inner.clone()) - // .map_err(RPolarsErr::from)?; - // Ok(out.into()) - // } + fn _meta_selector_xor(&self, other: &PlRExpr) -> Result { + let out = self + .inner + .clone() + .meta() + ._selector_xor(other.inner.clone()) + .map_err(RPolarsErr::from)?; + Ok(out.into()) + } fn _meta_as_selector(&self) -> Result { Ok(self.inner.clone().meta()._into_selector().into()) diff --git a/tests/testthat/test-selectors.R b/tests/testthat/test-selectors.R index 98cad23a..668b0676 100644 --- a/tests/testthat/test-selectors.R +++ b/tests/testthat/test-selectors.R @@ -42,6 +42,14 @@ test_that("'minus' operator works", { ) }) +test_that("'xor' operator works", { + df <- pl$DataFrame(foo = "a", bar = "b", foo3 = 3) + expect_named( + df$select(cs$string()$xor(cs$contains("foo"))), + c("bar", "foo3") + ) +}) + test_that("can use selectors in expressions", { df <- pl$DataFrame( dt = as.Date(c("1999-12-31", "2024-1-1", "2010-7-5")),