Search code examples
rpurrrrlang

how to iterate inside the elements of a quosure of rlang in R


So let's say that I want to now if X appears in the quosure.

library(rlang)
library(purrr)
q <- quo(mean(X))

I know I can check for equality with expr

q[[2]][[2]] == expr(X)
[1] TRUE

But how do I iterate or flatten the quo element? flatten(q) doesn't work, I couldn't use for loops, no idea how to use some map function from purrr.

Ideally I would like to capture X when it's "data" and not any function.


Solution

  • I use the following custom function to convert expressions to their Abstract Syntax Trees (ASTs):

    getAST <- function( ee ) { as.list(ee) %>% purrr::map_if(is.call, getAST) }
    

    Since you're working with quosures, there's an intermediate step of retrieving the associated expression:

    ## Define a quosure
    ## Side note: don't use q as a variable name; it conflicts with q()
    qsr <- quo( mean(5*X+2) )
    
    ## The associated expression
    xpr <- rlang::get_expr( qsr )
    
    ## ...and its AST
    ast <- getAST( xpr )
    # List of 2
    #  $ : symbol mean
    #  $ :List of 3
    #   ..$ : symbol +
    #   ..$ :List of 3
    #   .. ..$ : symbol *
    #   .. ..$ : num 5
    #   .. ..$ : symbol X
    #   ..$ : num 2
    

    From here, you can use standard techniques to find X. For example, flatten the nested list and compare each element to expr(X) as in your question:

    purrr::has_element( unlist(ast), expr(X) )
    # [1] TRUE
    
    purrr::map_lgl( unlist(ast), identical, expr(X) )
    # [1] FALSE FALSE FALSE FALSE  TRUE FALSE