rdplyr

False conditions in case_when causing error


I have a condition in case_when that should not be evaluated, but it is causing an error in my code. How can I only have this function run if the condition is TRUE?

library(dplyr)
#> 

tibble(x = "10+20)", 
       y = FALSE) |> 
  mutate(prior = case_when(y ~ as.character(eval(parse(text = x))),
                           T ~ as.character(x)))
#> Error in `mutate()`:
#> ℹ In argument: `prior = case_when(y ~ as.character(eval(parse(text =
#>   x))), T ~ as.character(x))`.
#> Caused by error in `case_when()`:
#> ! Failed to evaluate the right-hand side of formula 1.
#> Caused by error in `parse()`:
#> ! <text>:1:6: unexpected ')'
#> 1: 10+20)
#>          ^
#> Backtrace:
#>      ▆
#>   1. ├─dplyr::mutate(...)
#>   2. ├─dplyr:::mutate.data.frame(...)
#>   3. │ └─dplyr:::mutate_cols(.data, dplyr_quosures(...), by)
#>   4. │   ├─base::withCallingHandlers(...)
#>   5. │   └─dplyr:::mutate_col(dots[[i]], data, mask, new_columns)
#>   6. │     └─mask$eval_all_mutate(quo)
#>   7. │       └─dplyr (local) eval()
#>   8. ├─dplyr::case_when(y ~ as.character(eval(parse(text = x))), T ~ as.character(x))
#>   9. │ └─dplyr:::case_formula_evaluate(...)
#>  10. │   ├─base::withCallingHandlers(...)
#>  11. │   └─rlang::eval_tidy(pair$rhs, env = default_env)
#>  12. ├─base::eval(parse(text = x))
#>  13. ├─base::parse(text = x)
#>  14. └─base::.handleSimpleError(...)
#>  15.   └─dplyr (local) h(simpleError(msg, call))
#>  16.     └─rlang::abort(message, parent = cnd, call = error_call)

Created on 2024-09-06 with reprex v2.0.2


Solution

  • ifelse will not evaluate yes or no if the whole test vector is FALSE or TRUE respectively, so this works:

    tibble(x = "10+20)", 
           y = FALSE) |> 
      mutate(prior = ifelse(y, as.character(eval(parse(text = x))), as.character(x)))
    

    But you cannot do this element-wise, since you cannot partially evaluate a vector. So you need to use a non-vectorized if else structure in that case. E.g.

    library(purrr)
    tibble(x = c("10+20)", "10+20"), 
           y = c(FALSE, TRUE)) |> 
      mutate(prior = map2_chr(
        x, y, 
        \(x, y) if (y) as.character(eval(parse(text = x))) else x
      ))