How do I get the data from the selected rows of a filtered datatable (DT)?
While the accepted answer gives a working solution for shiny
, it wasn't obvious how to implement it inside an R Markdown document with flexdashboard
.
In Rmd
documents, render*()
functions like DT::renderDataTable()
are usually used anonymously, but it is possible to explicitly assign them to output
slots in shiny
. In this case, to use the input$tableID_rows_selected
construct, it's necessary to do so.
This solution also sorts the index to always maintain the same order as in the original data frame.
---
title: "MRE"
author: ""
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: scroll
runtime: shiny
---
Page {data-orientation=columns}
=======================================================================
Column {data-width=650}
-----------------------------------------------------------------------
### .
```{r}
require(DT)
library(dplyr)
library(tibble)
sliderInput("filter", label = "Filter by cyl", min = 4, max = 8, step = 2, value = 6)
filteredTable_data <- reactive({
mtcars %>% rownames_to_column() %>% ##dplyr's awkward way to preserve rownames
filter(., cyl == input$filter) %>% column_to_rownames()
})
##explicit assignment to output ID
DT::dataTableOutput("filteredTable")
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()[sort(ids),] ##sort index to ensure orig df sorting
})
##anonymous
DT::renderDataTable({
datatable(
filteredTable_selected(),
selection = list(mode = "none"),
caption = "Table that gets data from unfiltered original data"
)
})
```
And here's how the output looks like:
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"
)
})
}