Common Conditions

The sd_show_if() function to conditionally display a question and the sd_skip_forward() function to conditionally skip to a page each require that you provide a condition that returns a logical value (TRUE or FALSE) for each condition. This page highlights some of the most common types of conditions you might need.

Note

While we use the sd_show_if() function in most of these examples, the same logic applies to conditions used in the sd_skip_forward() function.

Conditioning on question answers

One of the most common situations is conditioning on the value of a single question or multiple questions, like this:

sd_show_if(

 # Simple condition based on single question choice
 input$penguins1 == "other" ~ "penguins1_other",

 # Multiple condition based on multiple question choices
 input$penguins2 == "other" & input$show_other == "show" ~ "penguins2_other"

)

In the first condition, the penguins1 question is checked to see if the respondent chose the "other" option. If they did, the penguins1_other question will be shown.

In the second condition, the penguins2 question is checked to see if the respondent chose the "other" option, and the show_other question is checked to see if the respondent chose the "show" option. With this condition, the penguins2_other question will only be shown if both conditions are TRUE.

Numeric values

Another common condition is checking the value of a numeric question. To do so, you need to wrap the input$question_id in the as.numeric() function because all question values are stored as strings, like this:

sd_show_if(
 as.numeric(input$car_number) > 1 ~ "car_ownership"
)

In the condition above, the car_number question is checked to see if the respondent chose a number greater than 1. If they did, the car_ownership question will be shown.

Multiple response questions

For multiple response question types (e.g. mc_multiple), the question returns a vector storing all the chosen values. You can use this vector to check for different conditions, such as whether the chosen values are in some set of values using the %in% operator, or whether the respondent chose a number of options using the length() function, like this:

sd_show_if(

 # Check if the respondent chose "apple", "banana", or both
 all(input$fav_fruits %in% c("apple", "banana")) ~ "apple_or_banana",

 # Check if the respondent chose more than 3 fruits
 length(input$fav_fruits) > 3 ~ "fruit_number"

)

In the first example, the fav_fruits question is checked to see if the respondent chose "apple", "banana", or both; if so, the apple_or_banana question will be shown.

In the second example, the fav_fruits question is checked to see if the respondent chose more than 3 fruits; if so, the fruit_number question will be shown.

Conditioning on answered status

You may want to show a target question if a question is answered at all or not. To do this, we created the sd_is_answered() function that returns TRUE if a question is answered and FALSE otherwise.

For example, let’s say you had a multiple choice question fav_fruit that asked you to choose your favorite fruit from a list of options, and a target question num_fruit that asked how many fruit you eat per day. If we wanted to show the num_fruit question so long as the fav_fruit question is answered, we can use sd_is_answered("fav_fruit") in the sd_show_if() function, like this:

sd_show_if(
  sd_is_answered("fav_fruit") ~ "num_fruit" 
)

This way, as long as the fav_fruit question is answered, no matter which option the user picks the num_fruit question will appear.

For "matrix" type questions, sd_is_answered() will only be TRUE if all sub-questions (matrix rows) in it are answered.

Conditioning with custom functions

For situations where the conditional logic is more complex, we recommend defining a custom function that will return a logical value (TRUE or FALSE). You can then pass this function to the sd_show_if() or sd_skip_forward() functions as a condition.

For example, let’s say we had a mc type question where we asked how many cars the respondent owned, and we included numeric options 1 through 5 as well as a final option "6 or more". If we wanted to set a condition that would return TRUE if the user had more than one car, using as.numeric(input$question_id) > 1 as the condition would be problematic, because this would return NA if the respondent chose the "6 or more" option.

To address this, we could create a custom function to handle this special condition:

server <- function(input, output, session) {

  more_than_one_car <- function(input) {
    if (is.null(input$car_number)) {
      return(FALSE)
    }
    num_cars <- suppressWarnings(as.numeric(input$car_number))
    if (is.na(num_cars)) {
      return(TRUE)
    }
    return(num_cars > 1)
  }

  sd_show_if(
    more_than_one_car(input) ~ "car_ownership"
  )

  sd_server(db = db)

}

In the more_than_one_car() function, we first return FALSE if the question is not yet answered (that’s the if (is.null(input$car_number)) part). Then we obtain the numeric value of the car_number question using as.numeric(input$car_number). We included a suppressWarnings() call here only because R will warn you if the result is NA by coercion. This will only happen if the respondent chose the "6 or more" option, in which case we return TRUE. If not, then we return the simple condition num_cars > 1 since we can safely know that the value of num_cars is a number.

Conditioning on custom values

Sometimes you’ll want to condition on values that aren’t directly related to question responses. For example, you might want to randomly assign respondents to different survey versions or experimental conditions.

Let’s say you have two versions of a page, e.g. page2a and page2b, and you want half of your respondents to see page 2a and the other half to see page 2b. You can accomplish this by:

  1. Generating a random condition value
  2. Storing that value for later use
  3. Using sd_skip_forward() to direct respondents to the appropriate page

Here’s how to implement this:

server <- function(input, output, session) {
  
  # Generate random condition
  a_or_b <- sample(c('a', 'b'), 1)
  
  # Store the condition value for later use if needed
  sd_store_value(a_or_b)
  
  # Use sd_skip_forward to direct to either page2a or page2b based on condition
  sd_skip_forward(
    a_or_b == 'a' ~ 'page2a',
    a_or_b == 'b' ~ 'page2b'
  )
  
  sd_server()
  
}

In this example, sample(c('a', 'b'), 1) randomly selects either 'a' or 'b'. The sd_store_value() function saves this value to the database, making it available for later analysis. Then, sd_skip_forward() uses this value to determine which page version the respondent should see.

This approach can be also be used for sd_show_if(). For example, here is how you could randomly assign a question to be hidden or shown:

server <- function(input, output, session) {
  # Create a static value to control question visibility
  show_condition <- sample(c(TRUE, FALSE), 1)

  # Store the static value for display in the UI
  sd_store_value(show_condition, "show_condition")

  # Show the conditional question based on the static value
  sd_show_if(
    show_condition ~ "conditional_question"
  )

  sd_server()
}

Conditioning on reactive expressions

The same behavior as the above example for conditioning on custom values can also be achieved using reactive expressions, using the sd_reactive() function. In this example, we define a reactive expressions, a_or_b, and then use it to control the conditional page skipping.

Note here that the conditions must be defined using a_or_b() instead of a_or_b - this is because in this example this object is a reactive expression rather than a static value and must be evaluated like a function call.

Note also that we don’t have to explicitly store the value in our data with a call to sd_store_value(). This is because the sd_reactive() function automatically stores the value in the data and will save it according to the specified id (in this case, "a_or_b").

server <- function(input, output, session) {

  a_or_b <- sd_reactive("a_or_b", {
    sample(c('a', 'b'), 1)
  })

  # Use sd_skip_forward to direct to either page2a or page2b based on condition
  sd_skip_forward(
    a_or_b() == 'a' ~ 'page2a',
    a_or_b() == 'b' ~ 'page2b'
  )

  sd_server()

}

shiny::shinyApp(ui = sd_ui(), server = server)

Likewise, the same concept can be applied to conditionally displaying a question based on a reactive expression:

server <- function(input, output, session) {

  # Create a reactive value that will control question visibility
  show_condition <- sd_reactive("show_condition", {
    sample(c(TRUE, FALSE), 1) # Randomly choose TRUE or FALSE
  })

  # Show the conditional question based on the reactive value
  sd_show_if(
    show_condition() ~ "conditional_question"
  )

  sd_server()
}
Back to top