Conditional Logic

This page talks about the sd_show_if() function to conditionally display Questions and Pages, and the sd_skip_forward() function to conditionally Skip to Pages in surveydown, followed by examples of Common Conditions that you can use with these functions. These functions allow you to control the flow of your survey based on the respondent’s answers.

Conditional Question Display

It is often useful to have a question display based on some condition, such as the respondent choosing a particular value in a multiple choice question. This can be achieved by the sd_show_if() function.

For example, let’s say we have a choice question about people’s favorite penguin type, and the last option is “other”. If the respondent chose it, you may want a second question to display that allows them to specify the “other” penguin type, like this:


To implement this, you first need to define both the conditional question and the target question in the survey.qmd file, like this:

```{r}
# Conditional question
sd_question(
  type  = "mc",
  id    = "penguins",
  label = "Which is your favorite type of penguin?",
  option = c(
    "Adélie"    = "adelie",
    "Chinstrap" = "chinstrap",
    "Gentoo"    = "gentoo",
    "Other"     = "other"
  )
)

# Target question
sd_question(
  type  = "text",
  id    = "penguins_other", 
  label = "Please specify the other penguin type:"
)
```

Then in the server function in the app.R file, you can use the sd_show_if() function to define that the "penguins_other" question would only be shown if the respondent chose the "other" option in the "penguins" question, like this:

Note

The input object is a Shiny object that stores each question id defined by sd_question() in your survey.qmd file, so whenever referring to a question in a condition, you must use the format input$question_id.

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

  sd_show_if(
    input$penguins == "other" ~ "penguins_other" 
  )

  sd_server(db = db)

}

The structure of the condition in the sd_show_if() function is always:

<condition> ~ "target_question_id"

You can provide multiple conditions to the sd_show_if() function, each separated by a comma.

In the example above, input$penguins == "other" is the condition, and "penguins_other" is the target question that will be shown if the condition is met. The ~ symbol is used to separate the condition from the target question.

Take a look at the Common Conditions section for examples of other types of supported conditions you can use to conditionally display questions.

Conditional Page Display

You can use sd_show_if() to also conditionally show whole pages using the same pattern:

sd_show_if(
  <condition> ~ "target_page_id"
)

One use case for this is a design where you want to randomly show respondents one of a set of pages. For example, let’s say you have versions A and B of page 2 in your survey, and you want to randomly show one to each respondent. Here’s a visual explanation of this setup:


To implement this, you first need to define each of the pages in your survey.qmd file, like this (on page 1 I’m using sd_output to display the randomly chosen value, A or B, which we’ll define in the server function below):

::: {.sd_page id=page1}

This is page 1

The next page will be page 2 `r sd_output("rand_val", type = "value")` 

```{r}
sd_next()
```

:::

::: {.sd_page id=page2a}

This is page 2A

```{r}
sd_next()
```

:::

::: {.sd_page id=page2b}

This is page 2B

```{r}
sd_next()
```

:::

::: {.sd_page id=page3}

This is page 3

```{r}
sd_close()
```

:::

Then in the server function in the app.R file, you can randomly generate a value to determine which of the page 2 version you’ll use (A or B), then condition on that value to display page2a or page2b using the sd_show_if() function, like this:

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

  # Generate random condition
  rand_val <- sample(c('A', 'B'), 1)

  # Store the condition value
  sd_store_value(rand_val)

  # Use sd_show_if to show target pages
  sd_show_if(
    rand_val == 'A' ~ 'page2a',
    rand_val == 'B' ~ 'page2b'
  )

  sd_server()

}

This approach will hide both page2a and page2b by default and only show the page if the condition on the left hand side is TRUE.

Note that we’re also using sd_store_value() here to store the random value, A or B, in the response data so that we can know later which page was shown for each respondent.

Conditional Forward Page Skipping

While basic page navigation is handled using sd_next(), you can override this static navigation in your server function with the sd_skip_forward() function to send the respondent to a forward page based on some condition.

A common example is the need to screen out people based on their response(s) to a question. Let’s say you need to screen out people who do not own a vehicle. To do this, you would first define a question in your survey.qmd file about their vehicle ownership, e.g.:

```{r}
sd_question(
  type  = 'mc',
  id    = 'vehicle_ownership',
  label = "Do you own your vehicle?",
  option = c(
    'Yes' = 'yes',
    'No'  = 'no'
  )
)
```

You would also need to define a screenout page to send respondents to, like this:

::: {.sd_page id=screenout}

Sorry, but you are not qualified to take our survey.

:::

Then in the server function in the app.R file, you can use the sd_skip_forward() function to define the condition under which the respondent will be sent to the target screenout page, like this:

Note

The input object is a Shiny object that stores each question id defined by sd_question() in your survey.qmd file, so whenever referring to a question in a condition, you must use the format input$question_id.

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

  sd_skip_forward(
    input$vehicle_ownership == "no" ~ "screenout"
  )

  # ...other server code...

}

You can provide multiple conditions to the sd_skip_forward() function, each separated by a comma. The structure for each condition is always:

<condition> ~ "target_page_id"

In the example above, input$vehicle_ownership == "no" is the condition, and "screenout" is the target page that the respondent will be sent to if the condition is met.

Take a look at the Common Conditions section for examples of other types of supported conditions you can use to conditionally control the survey flow.

Common Conditions

This section 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.

Question responses

One of the most common situations is conditioning on the response 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.

Answering 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.

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.

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.

A common example is the same situation as the Conditional Page Display section above where you have two versions of a page, 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, A or B
  2. Storing that value for later use
  3. Using sd_show_if() to conditionally show the correct page version

Here’s how to implement this:

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

  # Generate random condition
  rand_val <- sample(c('A', 'B'), 1)

  # Store the condition value
  sd_store_value(rand_val)

  # Use sd_show_if to show target pages
  sd_show_if(
    rand_val == 'A' ~ 'page2a',
    rand_val == '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_show_if() uses this value to determine which page version the respondent should see.

Back to top