reactable icon indicating copy to clipboard operation
reactable copied to clipboard

allow tidyselect for columGroups

Open werkstattcodes opened this issue 4 years ago • 2 comments

As previously mentioned, wonderful package! thx again.

It would be very convenient if columnGroups would allow selecting columns with tidyselect (contains, starts_with etc). Using the example from the pkg site, something like:

reactable(
  iris,
  columns = list(
    Sepal.Length = colDef(name = "Length"),
    Sepal.Width = colDef(name = "Width"),
    Petal.Length = colDef(name = "Length"),
    Petal.Width = colDef(name = "Width")
  ),
  columnGroups = list(
    colGroup(name = "Sepal", columns = contains("Sepal"),
    colGroup(name = "Petal", columns = contains("Petal")
  )
)

werkstattcodes avatar Oct 28 '20 17:10 werkstattcodes

Hi, that's an interesting idea, and it does look convenient. Could you use tidyselect to select columns outside of reactable? I'm totally unfamiliar with tidyselect and just skimmed the vignette at https://tidyselect.r-lib.org/articles/tidyselect.html. But it might be possible to write your own wrapper of reactable() that supports tidyselect syntax for the column groups:

# Wrapper for reactable that supports tidyselect syntax in columnGroups
# Based on https://cran.r-project.org/web/packages/tidyselect/vignettes/tidyselect.html
# NOTE: not sure if this is totally correct but it seems to work
reactable <- function(data, columnGroups = NULL, ...) {
  groups <- rlang::enquo(columnGroups)
  groups <- rlang::get_expr(groups)[-1]
  groups <- lapply(groups, function(group) {
    pos <- tidyselect::eval_select(group$columns, data = data)
    group$columns <- names(pos)
    rlang::eval_tidy(group)
  })
  reactable::reactable(data, columnGroups = groups, ...)
}

Then you could use the drop-in reactable() replacement like:

library(reactable)
library(tidyselect)

reactable(
  iris,
  columns = list(
    Sepal.Length = colDef(name = "Length"),
    Sepal.Width = colDef(name = "Width"),
    Petal.Length = colDef(name = "Length"),
    Petal.Width = colDef(name = "Width")
  ),
  columnGroups = list(
    colGroup(name = "Sepal", columns = contains("Sepal")),
    colGroup(name = "Petal", columns = contains("Petal"))
  )
)

While the syntax is convenient, I don't feel particularly strongly about building it into reactable. For this specific example, you could also use grep() to select columns that contain or start with some pattern like:

reactable(
  iris,
  columns = list(
    Sepal.Length = colDef(name = "Length"),
    Sepal.Width = colDef(name = "Width"),
    Petal.Length = colDef(name = "Length"),
    Petal.Width = colDef(name = "Width")
  ),
  columnGroups = list(
    colGroup(name = "Sepal", columns = grep("Sepal", names(iris), value = TRUE)),
    colGroup(name = "Petal", columns = grep("^Petal", names(iris), value = TRUE))
  )
)

glin avatar Nov 01 '20 17:11 glin

A simple workaround that has helped me is using str_subset() on the column names e.g.

reactable(
  iris,
  columns = list(
    Sepal.Length = colDef(name = "Length"),
    Sepal.Width = colDef(name = "Width"),
    Petal.Length = colDef(name = "Length"),
    Petal.Width = colDef(name = "Width")
  ),
  columnGroups = list(
    colGroup(name = "Sepal", columns = str_subset(colnames(iris), "Sepal"),
    colGroup(name = "Petal", columns = str_subset(colnames(iris), "Petal")
  )
)

MokeEire avatar Nov 05 '21 19:11 MokeEire