diff --git a/R/tab_create_modify.R b/R/tab_create_modify.R index 83601a2bc6..ecdd916d6d 100644 --- a/R/tab_create_modify.R +++ b/R/tab_create_modify.R @@ -800,8 +800,8 @@ resolve_spanned_column_names <- function( #' #' The `tab_spanner_delim()` function can take specially-crafted column names #' and generate one or more spanners (and revise column labels at the same -#' time). This is done by splitting the column name by a specified delimiter -#' (this is the `delim`) and placing the fragments from top to bottom (i.e., +#' time). This is done by splitting the column name by the specified delimiter +#' text (`delim`) and placing the fragments from top to bottom (i.e., #' higher-level spanners to the column labels) or vice versa. Furthermore, #' neighboring text fragments on different spanner levels that have the same #' text will be coalesced together. For instance, having the three side-by-side @@ -811,16 +811,16 @@ resolve_spanned_column_names <- function( #' `cols_spanner_delim()` to slice and dice delimited column names in different #' ways: #' -#' - the delimiter: choose which delimiter to use for the fragmentation of +#' - delimiter text: choose the delimiter text to use for the fragmentation of #' column names into spanners with the `delim` argument #' - direction and amount of splitting: we can choose to split *n* times #' according to a `limit` argument, and, we get to specify from which side of -#' the column name the splitting should occur +#' the column name the splitting should commence #' - reversal of fragments: we can reverse the order the fragments we get from -#' the splitting procedure -#' - column constraints: define which columns in a **gt** table that should -#' participate in spanner creation using vectors or **tidyselect**-style -#' expressions +#' the splitting procedure with the `reverse` argument +#' - column constraints: it's possible to constrain which columns in a **gt** +#' table should participate in spanner creation using vectors or +#' **tidyselect**-style expressions #' #' @inheritParams tab_spanner #' @@ -828,8 +828,8 @@ resolve_spanned_column_names <- function( #' #' `scalar` // **required** #' -#' The delimiter to use to split an input column name. This should be a single -#' character (e.g., `"_"`, `"."`, etc.). +#' The delimiter text to use to split one of more column names (i.e., those +#' that are targeted via the `columns` argument). #' #' @param columns *Columns to target* #' @@ -1118,8 +1118,8 @@ tab_spanner_delim <- function( cli::cli_abort("`delim` must be a single value.") } - if (nchar(delim) != 1) { - cli::cli_abort("The value supplied for `delim` must be a single character.") + if (nchar(delim) < 1) { + cli::cli_abort("The value supplied for `delim` must be at least a single character.") } # Get all of the columns in the dataset @@ -1323,6 +1323,8 @@ str_split_across <- function( reverse = FALSE ) { + delim_width <- nchar(delim) + if (is.null(n)) { x_split <- unlist(strsplit(x, split = delim, fixed = TRUE)) @@ -1353,7 +1355,6 @@ str_split_across <- function( x_split <- x for (i in seq_len(n)) { - if (split == "last") { x_split_i <- x_split[1] @@ -1370,7 +1371,7 @@ str_split_across <- function( x_split_n <- nchar(x_split_i) x_split_1 <- substr(x_split_i, start = 1, stop = split_delim - 1) - x_split_2 <- substr(x_split_i, start = split_delim + 1, x_split_n) + x_split_2 <- substr(x_split_i, start = split_delim + delim_width, x_split_n) x_split <- c(x_split_1, x_split_2, x_split) @@ -1390,7 +1391,7 @@ str_split_across <- function( x_split_n <- nchar(x_split_i) x_split_1 <- substr(x_split_i, start = 1, stop = split_delim - 1) - x_split_2 <- substr(x_split_i, start = split_delim + 1, x_split_n) + x_split_2 <- substr(x_split_i, start = split_delim + delim_width, x_split_n) x_split <- c(x_split, x_split_1, x_split_2) } diff --git a/man/cell_borders.Rd b/man/cell_borders.Rd index 271806c65a..1e933a36d5 100644 --- a/man/cell_borders.Rd +++ b/man/cell_borders.Rd @@ -20,16 +20,16 @@ can use the \code{"all"} option.} \verb{scalar|NULL} // \emph{default:} \code{"#000000"} The border \code{color} can be defined with a color name or with a hexadecimal -color code. The default \code{color} value is \code{"#000000"} (black). Borders for any -defined \code{sides} can be removed by supplying \code{NULL} here.} +color code. The default \code{color} value is \code{"#000000"} (black). Borders for +any defined \code{sides} can be removed by supplying \code{NULL} here.} \item{style}{\emph{Border line style} \verb{scalar|NULL} // \emph{default:} \code{"solid"} The border \code{style} can be one of either \code{"solid"} (the default), -\code{"dashed"}, \code{"dotted"}, \code{"hidden"}, or \code{"double"}. Borders for any -defined \code{sides} can be removed by supplying \code{NULL} here.} +\code{"dashed"}, \code{"dotted"}, \code{"hidden"}, or \code{"double"}. Borders for any defined +\code{sides} can be removed by supplying \code{NULL} here.} \item{weight}{\emph{Border weight} diff --git a/man/tab_spanner_delim.Rd b/man/tab_spanner_delim.Rd index d56c3ffbb5..0eb28198a1 100644 --- a/man/tab_spanner_delim.Rd +++ b/man/tab_spanner_delim.Rd @@ -25,8 +25,8 @@ This is the \strong{gt} table object that is commonly created through use of the \verb{scalar} // \strong{required} -The delimiter to use to split an input column name. This should be a single -character (e.g., \code{"_"}, \code{"."}, etc.).} +The delimiter text to use to split one of more column names (i.e., those +that are targeted via the \code{columns} argument).} \item{columns}{\emph{Columns to target} @@ -74,8 +74,8 @@ An object of class \code{gt_tbl}. \description{ The \code{tab_spanner_delim()} function can take specially-crafted column names and generate one or more spanners (and revise column labels at the same -time). This is done by splitting the column name by a specified delimiter -(this is the \code{delim}) and placing the fragments from top to bottom (i.e., +time). This is done by splitting the column name by the specified delimiter +text (\code{delim}) and placing the fragments from top to bottom (i.e., higher-level spanners to the column labels) or vice versa. Furthermore, neighboring text fragments on different spanner levels that have the same text will be coalesced together. For instance, having the three side-by-side @@ -85,16 +85,16 @@ the labels \code{"1"}, \code{"2"}, and \code{"3"}. There are many options in \code{cols_spanner_delim()} to slice and dice delimited column names in different ways: \itemize{ -\item the delimiter: choose which delimiter to use for the fragmentation of +\item delimiter text: choose the delimiter text to use for the fragmentation of column names into spanners with the \code{delim} argument \item direction and amount of splitting: we can choose to split \emph{n} times according to a \code{limit} argument, and, we get to specify from which side of -the column name the splitting should occur +the column name the splitting should commence \item reversal of fragments: we can reverse the order the fragments we get from -the splitting procedure -\item column constraints: define which columns in a \strong{gt} table that should -participate in spanner creation using vectors or \strong{tidyselect}-style -expressions +the splitting procedure with the \code{reverse} argument +\item column constraints: it's possible to constrain which columns in a \strong{gt} +table should participate in spanner creation using vectors or +\strong{tidyselect}-style expressions } } \section{Details on column splitting}{ diff --git a/tests/testthat/_snaps/tab_spanner_delim.md b/tests/testthat/_snaps/tab_spanner_delim.md index b0e0be91fe..e8f37dd564 100644 --- a/tests/testthat/_snaps/tab_spanner_delim.md +++ b/tests/testthat/_snaps/tab_spanner_delim.md @@ -47,6 +47,76 @@ Output [1] "\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n\n\n \n \n \n
\n all\n \n all\n
\n W\n all.X.Ball.Y.A\n Z\n
AB
1234
" +--- + + Code + . + Output + [1] "\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n\n\n \n \n \n
\n all\n
\n W\n \n X\n \n Y\n \n Z\n
ABAB
1234
" + +--- + + Code + . + Output + [1] "\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n\n\n \n \n \n
\n all\n
\n W\n \n X\n \n Y\n \n Z\n
ABAB
1234
" + +--- + + Code + . + Output + [1] "\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n\n\n \n \n \n
\n all\n
W__1AX__1BY__1AZ__1B
1234
" + +--- + + Code + . + Output + [1] "\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n\n\n \n \n \n
\n all\n
\n W\n \n X\n \n Y\n \n Z\n
ABAB
1234
" + +--- + + Code + . + Output + [1] "\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n\n\n \n \n \n
\n all__1W\n \n all__1X\n \n all__1Y\n \n all__1Z\n
ABAB
1234
" + +--- + + Code + . + Output + [1] "\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n\n\n \n \n \n
\n all\n
\n W\n \n X\n \n Y\n \n Z\n
ABAB
1234
" + +--- + + Code + . + Output + [1] "\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n\n\n \n \n \n
\n A\n \n B\n \n A\n \n B\n
\n W\n \n X\n \n Y\n \n Z\n
allallallall
1234
" + +--- + + Code + . + Output + [1] "\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n\n\n \n \n \n
\n A\n \n B\n \n A\n \n B\n
\n W\n \n X\n \n Y\n \n Z\n
allallallall
1234
" + +--- + + Code + . + Output + [1] "\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n\n\n \n \n \n
\n A\n \n B\n \n A\n \n B\n
all__1Wall__1Xall__1Yall__1Z
1234
" + +--- + + Code + . + Output + [1] "\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n\n\n \n \n \n
\n W__1A\n \n X__1B\n \n Y__1A\n \n Z__1B\n
allallallall
1234
" + --- Code diff --git a/tests/testthat/test-tab_spanner_delim.R b/tests/testthat/test-tab_spanner_delim.R index 7db20d01b9..01d045ae46 100644 --- a/tests/testthat/test-tab_spanner_delim.R +++ b/tests/testthat/test-tab_spanner_delim.R @@ -243,20 +243,12 @@ test_that("The `tab_spanner_delim()` function works correctly", { ) # Expect an error if an invalid delimiter specification is used; here - # we use strings that aren't a single character (and some cases, vectors - # with lengths not equal to one) - expect_error( - gt(iris_short) %>% - tab_spanner_delim(delim = "__") - ) + # we use strings that aren't at least a single character (and, in some cases, + # vectors with lengths not equal to one) expect_error( gt(iris_short) %>% tab_spanner_delim(delim = "") ) - expect_error( - gt(iris_short) %>% - tab_spanner_delim(delim = " ") - ) expect_error( gt(iris_short) %>% tab_spanner_delim(delim = c(".", ".")) @@ -767,7 +759,7 @@ test_that("`tab_spanner_delim()` works on higher-order spanning", { gt(tbl_5) %>% tab_spanner_delim(delim = ".") - # Take snapshots of `gt_tbl_5a` + # Take snapshot of `gt_tbl_5a` gt_tbl_5a %>% render_as_html() %>% expect_snapshot() gt_tbl_5b <- @@ -777,7 +769,7 @@ test_that("`tab_spanner_delim()` works on higher-order spanning", { columns = c(all.W.A, all.X.B) ) - # Take snapshots of `gt_tbl_5b` + # Take snapshot of `gt_tbl_5b` gt_tbl_5b %>% render_as_html() %>% expect_snapshot() gt_tbl_5c <- @@ -787,9 +779,90 @@ test_that("`tab_spanner_delim()` works on higher-order spanning", { columns = c(all.W.A, all.Z.B) ) - # Take snapshots of `gt_tbl_5c` + # Take snapshot of `gt_tbl_5c` gt_tbl_5c %>% render_as_html() %>% expect_snapshot() + # Generate a table with delimiters that are composed of several characters + # and are asymmetric with regard to the ordering of characters + tbl_5m <- + dplyr::tibble( + all__1W__1A = 1, + all__1X__1B = 2, + all__1Y__1A = 3, + all__1Z__1B = 4 + ) + + gt_tbl_5m_a <- + gt(tbl_5m) %>% + tab_spanner_delim(delim = "__1", split = "last") + + # Take snapshot of `gt_tbl_5m_a` + gt_tbl_5m_a %>% render_as_html() %>% expect_snapshot() + + gt_tbl_5m_b <- + gt(tbl_5m) %>% + tab_spanner_delim(delim = "__1", split = "first") + + # Take snapshot of `gt_tbl_5m_b` + gt_tbl_5m_b %>% render_as_html() %>% expect_snapshot() + + gt_tbl_5m_c <- + gt(tbl_5m) %>% + tab_spanner_delim(delim = "__1", split = "first", limit = 1) + + # Take snapshot of `gt_tbl_5m_c` + gt_tbl_5m_c %>% render_as_html() %>% expect_snapshot() + + gt_tbl_5m_d <- + gt(tbl_5m) %>% + tab_spanner_delim(delim = "__1", split = "first", limit = 2) + + # Take snapshot of `gt_tbl_5m_d` + gt_tbl_5m_d %>% render_as_html() %>% expect_snapshot() + + gt_tbl_5m_e <- + gt(tbl_5m) %>% + tab_spanner_delim(delim = "__1", split = "last", limit = 1) + + # Take snapshot of `gt_tbl_5m_e` + gt_tbl_5m_e %>% render_as_html() %>% expect_snapshot() + + gt_tbl_5m_f <- + gt(tbl_5m) %>% + tab_spanner_delim(delim = "__1", split = "last", limit = 2) + + # Take snapshot of `gt_tbl_5m_f` + gt_tbl_5m_f %>% render_as_html() %>% expect_snapshot() + + gt_tbl_5m_g <- + gt(tbl_5m) %>% + tab_spanner_delim(delim = "__1", split = "last", limit = 2, reverse = TRUE) + + # Take snapshot of `gt_tbl_5m_g` + gt_tbl_5m_g %>% render_as_html() %>% expect_snapshot() + + gt_tbl_5m_h <- + gt(tbl_5m) %>% + tab_spanner_delim(delim = "__1", split = "first", limit = 2, reverse = TRUE) + + # Take snapshot of `gt_tbl_5m_h` + gt_tbl_5m_h %>% render_as_html() %>% expect_snapshot() + + gt_tbl_5m_i <- + gt(tbl_5m) %>% + tab_spanner_delim(delim = "__1", split = "last", limit = 1, reverse = TRUE) + + # Take snapshot of `gt_tbl_5m_i` + gt_tbl_5m_i %>% render_as_html() %>% expect_snapshot() + + gt_tbl_5m_j <- + gt(tbl_5m) %>% + tab_spanner_delim(delim = "__1", split = "first", limit = 1, reverse = TRUE) + + # Take snapshot of `gt_tbl_5m_j` + gt_tbl_5m_j %>% render_as_html() %>% expect_snapshot() + + # # Combinations of `tab_spanner_delim()` and `tab_spanner()` to # generate otherwise tricky spanner constructions