Applying multiple filters for a reactive table

I'm wondering if anyone could suggest a better (and more scalable) approach to the example below. In general, I'm looking to apply multiple different filters to a data frame, which will then be rendered. On initial load, the filters will be empty so the full data frame will be returned. As inputs, that will be used as filters, are filled in they are applied to the data frame. I've accomplish this will various if statements to check for inputs but this doesn't seem like the most elegant solution as the number of inputs grows.

library(shiny)
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union

# quick data processing
mtcarsDf <- mtcars %>%
    mutate(car_name = row.names(mtcars)) %>%
    select(car_name, cyl, mpg, gear)

ui <- fluidPage(
    titlePanel(""),
    
    sidebarLayout(
        sidebarPanel(
            textInput("name",
                      "Name"),
            selectInput("gear",
                        "Gears",
                        choices = mtcarsDf$gear
            ),
            selectInput("cyl",
                        "Cycles",
                        choices = mtcarsDf$cyl)
        ),
        mainPanel(
            DT::dataTableOutput("table"),
            plotOutput("plot")
        )
    )
)

server <- function(input, output, session) {
    
    reactiveDf <- reactive({
        if (input$name == "" &
            input$cyl == "") {
            return(mtcarsDf)
        }
        
        if (input$name != "") {
            mtcarsDf <- mtcarsDf %>%
                filter(
                    grepl(input$name, car_name, ignore.case = TRUE)
                )
            
        }
        
        if (input$cyl != "") {
            mtcarsDf <- mtcarsDf %>%
                filter(
                    cyl == input$cyl
                )
            
        }
        
        return(mtcarsDf)
    })
    
    output$table <- DT::renderDataTable({
        reactiveDf()
    })
    
}

shinyApp(ui, server)

Did you ever figure out a good solution for this?

At the moment, I have not. Definitely would be interested to know if anyone has though.

I've used a cascading approach in some apps. For each filter I have a reactive object in between that filters based on the previous selection, providing only those choices to the next filter. It works, but only in one direction. Besides a lot of if/then work I'm not sure how to have a set of X filters that all respond to the others in any order. I'm surprised such a solution doesn't already exist.

I'm not sure how scalable you consider this to be, but perhaps something like the following would suit your needs:

conditional <- function(condition, success) {
    if (condition) success else TRUE
}

reactiveDf <- reactive({
  mtcarsDf %>%
    filter(
      conditional(input$name != "", grepl(input$name, car_name, ignore.case = TRUE)),
      conditional(input$gear != "", gear == input$gear),
      conditional(input$cyl != "", cyl == input$cyl)
    )
})

Evidently, the conditional() function only applies the filter condition provided via success if the given condition evaluates to TRUE. By returning TRUE when condition fails, you are essentially telling dplyr::filter() to keep all rows; this is because of the way the ... is used in dplyr::filter(), namely:

Multiple conditions are combined with & . Only rows where the condition evaluates to TRUE are kept.

Let me know if this explanation isn't clear enough; I'd be happy to elaborate.

Hope this helps!

@hugo-pa This is great! Definitely a much cleaner solution than a bunch of if-else statements. Thanks!

1 Like

My pleasure; glad I could help!

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.