The DT package allows you to get the indices of selected rows using input$tableID_rows_selected
. This works great for tables that do not have filtered data. However, if we have a filtered dataset, we can't use this same approach, as the row indices are off.
For a filtered dataset, then, how would we get the data in the selected rows of a datatable?
Below, I've posted a basic shiny app that shows four tables: the first one is the original mtcars dataset and the second gets the selected rows in the first. The third and the fourth do the same thing, but after filtering the dataset on the "filter" sliderInput.
library(shiny)
library(DT)
library(dplyr)
ui <- fluidPage(
DT::dataTableOutput("origTable"),
DT::dataTableOutput("origTableSelected"),
sliderInput("filter", label = "Filter by cyl", min = 4, max = 8, step = 2, value = 6),
DT::dataTableOutput("filteredTable"),
DT::dataTableOutput("filteredTableSelected")
)
server <- function(input, output, session) {
output$origTable <- DT::renderDataTable({
datatable(
mtcars,
selection = list(mode = "multiple"),
caption = "Original Data"
)
})
origTable_selected <- reactive({
ids <- input$origTable_rows_selected
mtcars[ids,]
})
output$origTableSelected <- DT::renderDataTable({
datatable(
origTable_selected(),
selection = list(mode = "multiple"),
caption = "Selected Rows from Original Data Table"
)
})
output$filteredTable <- DT::renderDataTable({
datatable(
filter(mtcars, cyl == input$filter),
selection = list(mode = "multiple"),
caption = "Filtered Table (based on cyl)"
)
})
filteredTable_selected <- reactive({
ids <- input$filteredTable_rows_selected
mtcars[ids,]
})
output$filteredTableSelected <- DT::renderDataTable({
datatable(
filteredTable_selected(),
selection = list(mode = "none"),
caption = "Table that gets data from unfiltered original data"
)
})
}
shinyApp(ui = ui, server = server)
One way: in your filteredTable_selected() function, where you're creating the data you'll put in your fourth DT, use filter(mtcars, cyl == input$filter)
like you did for your third table instead of mtcars
. This way, the row indices will match.
If you're worried about performance issues on larger datsets, just filter the data in a reactive expression, which caches its output. This way, you won't filter more than your input$filter value changes.
server <- function(input, output, session) {
filteredTable_data <- reactive({
filter(mtcars, cyl == input$filter)
})
output$filteredTable <- DT::renderDataTable({
datatable(
filteredTable_data(),
selection = list(mode = "multiple"),
caption = "Filtered Table (based on cyl)"
)
})
filteredTable_selected <- reactive({
ids <- input$filteredTable_rows_selected
filteredTable_data()[ids,]
})
output$filteredTableSelected <- DT::renderDataTable({
datatable(
filteredTable_selected(),
selection = list(mode = "none"),
caption = "Table that gets data from unfiltered original data"
)
})
}