Search code examples
rdplyrpurrrmagrittr

piping a vector into all() to test equality


I'm trying to pipe a vector into an all() statement to check if all elements are equal to a certain value. I figure I need to use the exposition pipe %$% since all() does not have a built-in data argument. My attempt leads to an error:

library(tidyverse)
library(magrittr)

vec <- c("a", "b", "a")

vec %>%
  keep(!grepl("b", .)) %$%
  all(. == "a")
#> Error in eval(substitute(expr), data, enclos = parent.frame()): invalid 'envir' argument of type 'character'

If I break the pipe before all() and assign the output to an object p, and then pass p to all() as a second command it works fine:

vec %>%
  keep(!grepl("b", .)) -> p

all(p == "a")
#> [1] TRUE

I don't understand why this works while my first attempt does not. I'd like to be able to do this in a single pipe that results in TRUE.

If vec is instead a tibble the following works:

vec <- tibble(var = c("a", "b", "a"))

vec %>%
  filter(!grepl("b", var)) %$%
  all(.$var == "a")
#> [1] TRUE

This doesn't suit my purposes as well, and I'd for my own understanding to know why my first attempt does not work.


Solution

  • The way pipe works is it takes the left-hand side of the pipe operator and passes it as a first argument to the right hand side function. So here, in this case as we need to modify the data argument to all , we need to stop pipe from passing the LHS to the RHS. We can do that by using {}.

    library(magrittr)
    
    vec %>% purrr::keep(!grepl("b", .)) %>% {all(. == 'a')}
    #[1] TRUE
    

    In vec , let's check if all the elements are either "a" or "b". We can use %in% here.

    vec <- c("a", "b", "a")
    

    Plain version without pipes would be :

    all(vec %in% c('a', 'b'))
    #[1] TRUE
    

    With pipes, if we try

    vec %>% all(. %in% c('a', 'b'))
    

    We get

    #[1] NA
    

    Warning message: In all(., . %in% c("a", "b")) : coercing argument of type 'character' to logical

    what is happening here is

    all(vec, vec %in% c('a', 'b'))
    #[1] NA
    

    Warning message: In all(vec, vec %in% c("a", "b")) : coercing argument of type 'character' to logical

    which returns the same message.

    To avoid this we use {}

    vec %>% {all(. %in% c('a', 'b'))}
    #[1] TRUE
    

    which gives us the expected answer.