glin / reactable

Interactive data tables for R
https://glin.github.io/reactable
Other
629 stars 80 forks source link

filter column with 2 levels #384

Closed guillaumechaumet closed 1 month ago

guillaumechaumet commented 2 months ago

I use the following code to filter 4 columns:

pathogenicity_label = colDef(
  na = "Not Classified",
  filterInput = function(values, name) {
  tags$select(
    onchange = sprintf("Reactable.setFilter('genomtab', '%s', event.target.value || undefined)", name),
    tags$option(value = "", "All"),
    purrr::map(sort(unique(values)), tags$option),
    "aria-label" = sprintf("Filter %s", name),
    style = "width: 100%; height: 28px;"
     )
  }
)

It work for every columns but not for 1 column which only have 2 levels. When I choose the first level, It has the same result as "All".

Is it a known bug ?

Edit:

Here, a simple code with this bug


library(reactable)
library(htmltools)
mytable = data.frame(group = c(rep('HP',26),rep('NHP',26)),var1 = rep(LETTERS,2), rnorm(52,mean = 30,sd = 10))

htmltools::browsable(
  tagList(
    reactable(mytable,
      searchable = TRUE,
      highlight = TRUE,
      bordered = TRUE,
      rowStyle = list(cursor = "pointer"),
      defaultPageSize = 5,
      striped = TRUE,
      wrap = FALSE,
      filterable = TRUE,
      columns = list(
        group = colDef(
          filterInput = function(values, name) {
            tags$select(
            onchange = sprintf("Reactable.setFilter('mytab', '%s', event.target.value || undefined)", name),
            tags$option(value = "", "All"),
            lapply(sort(unique(values)), tags$option),
            "aria-label" = sprintf("Filter %s", name),
            style = "width: 100%; height: 28px;"
          )
        }
      )
      ),
      elementId = "mytab"
    )
  )
)
radovan-miletic commented 2 months ago

According to ChatGPT we should apply custom filter method to ensure that the filtering is based on exact matches (only rows with exact matches to the filter value are returned), which should resolve the issue with overlapping substrings like 'HP' and 'NHP' or 'AB' and 'CAB'.

filterMethod = JS("function(rows, columnId, filterValue) {
                    return rows.filter(function(row) {
                      if (filterValue === '') {
                        return true;
                      }
                      return row.values[columnId] === filterValue;
                    });
                  }")

It seems to work well but Greg's opinion or someone's from {reactable} community experienced in JS is needed !

library(reactable)
library(htmltools)

mytable <- data.frame(group = c(rep('AB', 26), rep('CAB', 26)), var1 = rep(LETTERS, 2), rnorm(52, mean = 30, sd = 10))

htmltools::browsable(
  tagList(
    reactable(mytable,
              searchable = TRUE,
              highlight = TRUE,
              bordered = TRUE,
              rowStyle = list(cursor = "pointer"),
              defaultPageSize = 5,
              striped = TRUE,
              wrap = FALSE,
              filterable = TRUE,
              columns = list(
                group = colDef(
                  filterInput = function(values, name) {
                    tags$select(
                      id = sprintf("filter-%s", name),
                      onchange = sprintf("Reactable.setFilter('mytab', '%s', event.target.value || undefined)", name),
                      tags$option(value = "", "All"),
                      lapply(sort(unique(values)), function(value) {
                        tags$option(value = value, value)
                      }),
                      "aria-label" = sprintf("Filter %s", name),
                      style = "width: 100%; height: 28px;"
                    )
                  },
                  filterMethod = JS("function(rows, columnId, filterValue) {
                    return rows.filter(function(row) {
                      if (filterValue === '') {
                        return true;
                      }
                      return row.values[columnId] === filterValue;
                    });
                  }")
                )
              ),
              elementId = "mytab"
    )
  )
)
guillaumechaumet commented 2 months ago

Thank you for your accurate response.

Le ven. 26 juil. 2024 à 13:17, Radovan Miletić @.***> a écrit :

According to ChatGPT we should apply custom filter method to ensure that the filtering is based on exact matches (only rows with exact matches to the filter value are returned), which should resolve the issue with overlapping substrings like 'HP' and 'NHP' or 'AB' and 'CAB'.

filterMethod = JS("function(rows, columnId, filterValue) { return rows.filter(function(row) { if (filterValue === '') { return true; } return row.values[columnId] === filterValue; }); }")

It seems to work well but Greg's opinion or someone's from {reactable} community experienced in JS is needed !

library(reactable) library(htmltools)

mytable <- data.frame(group = c(rep('AB', 26), rep('CAB', 26)), var1 = rep(LETTERS, 2), rnorm(52, mean = 30, sd = 10))

htmltools::browsable( tagList( reactable(mytable, searchable = TRUE, highlight = TRUE, bordered = TRUE, rowStyle = list(cursor = "pointer"), defaultPageSize = 5, striped = TRUE, wrap = FALSE, filterable = TRUE, columns = list( group = colDef( filterInput = function(values, name) { tags$select( id = sprintf("filter-%s", name), onchange = sprintf("Reactable.setFilter('mytab', '%s', event.target.value || undefined)", name), tags$option(value = "", "All"), lapply(sort(unique(values)), function(value) { tags$option(value = value, value) }), "aria-label" = sprintf("Filter %s", name), style = "width: 100%; height: 28px;" ) }, filterMethod = JS("function(rows, columnId, filterValue) { return rows.filter(function(row) { if (filterValue === '') { return true; } return row.values[columnId] === filterValue; }); }") ) ), elementId = "mytab" ) ) )

— Reply to this email directly, view it on GitHub https://github.com/glin/reactable/issues/384#issuecomment-2252543483, or unsubscribe https://github.com/notifications/unsubscribe-auth/ADVLQ2AKMNRPUBUAA5XYC6DZOIV5ZAVCNFSM6AAAAABLKTQPSGVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDENJSGU2DGNBYGM . You are receiving this because you authored the thread.Message ID: @.***>

glin commented 2 months ago

@radovan-miletic Thanks for answering. Yep, the default filter method is a case-insensitive string match like grep(..., ignore.case = TRUE), so you'll need that custom filter method for exact matches. I just noticed we're missing an example for exact match filtering at https://glin.github.io/reactable/articles/custom-filtering.html#examples, so I'll get that added at some point.

glin commented 1 month ago

Added an example in https://github.com/glin/reactable/commit/2b69b49ebc7cad5b6649e0593378be10ed9969bd