Skip to content

Commit

Permalink
add worksheet name to cell_limits class; see #8
Browse files Browse the repository at this point in the history
  • Loading branch information
jennybc committed Apr 6, 2016
1 parent af1f830 commit 708d349
Show file tree
Hide file tree
Showing 16 changed files with 137 additions and 48 deletions.
4 changes: 3 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ Authors@R: c(
)
Description: Helper functions to work with spreadsheets and the "A1:D10" style
of cell range specification.
Depends: R (>= 3.0.0)
Depends:
R (>= 3.0.0)
License: MIT + file LICENSE
LazyData: true
URL: https://github.com/jennybc/cellranger
BugReports: https://github.com/jennybc/cellranger/issues
Suggests:
covr,
testthat
RoxygenNote: 5.0.1.9000
2 changes: 1 addition & 1 deletion NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by roxygen2 (4.1.1): do not edit by hand
# Generated by roxygen2: do not edit by hand

S3method(as.cell_limits,"NULL")
S3method(as.cell_limits,cell_limits)
Expand Down
81 changes: 52 additions & 29 deletions R/cell-limits.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
#' operations on a spreadsheet. Downstream code can be written assuming cell
#' limits are stored in a valid \code{cell_limits} object.
#'
#' A \code{cell_limits} object is a list with two components:
#' A \code{cell_limits} object is a list with three components:
#'
#' \itemize{
#' \item \code{ul} vector specifying upper left cell of target rectangle, of
#' the form \code{c(ROW_MIN, COL_MIN)}
#' \item \code{lr} vector specifying lower right cell of target rectangle, of
#' the form \code{c(ROW_MAX, COL_MAX)}
#' \item \code{wsn} string specifying worksheet name, which may be \code{NA},
#' meaning it's unspecified
#' }
#'
#' This follows the spreadsheet convention where a cell range is described as
Expand All @@ -29,6 +31,7 @@
#'
#' @param ul vector identifying upper left cell of target rectangle
#' @param lr vector identifying lower right cell of target rectangle
#' @param wsn string containing worksheet name, optional
#' @param x input to convert into a \code{cell_limits} object
#'
#' @return a \code{cell_limits} object
Expand All @@ -39,13 +42,17 @@
#' cell_limits(c(NA, 7))
#' cell_limits(lr = c(3, 7))
#'
#' cell_limits(c(1, 3), c(1, 5), "Sheet1")
#' cell_limits(c(1, 3), c(1, 5), "Spaces are evil")
#'
#' dim(as.cell_limits("A1:F10"))
#'
#' @export
cell_limits <- function(ul = c(NA_integer_, NA_integer_),
lr = c(NA_integer_, NA_integer_)) {
lr = c(NA_integer_, NA_integer_),
wsn = NA_character_) {

stopifnot(length(ul) == 2L, length(lr) == 2L)
stopifnot(length(ul) == 2L, length(lr) == 2L, length(wsn) == 1L)

ul <- as.integer(ul)
lr <- as.integer(lr)
Expand All @@ -54,16 +61,16 @@ cell_limits <- function(ul = c(NA_integer_, NA_integer_),
stopifnot(all(NA_or_pos(ul)))
stopifnot(all(NA_or_pos(lr)))

if(is.na(ul[1]) && !is.na(lr[1])) ul[1] <- 1L
if(is.na(ul[2]) && !is.na(lr[2])) ul[2] <- 1L
if (is.na(ul[1]) && !is.na(lr[1])) ul[1] <- 1L
if (is.na(ul[2]) && !is.na(lr[2])) ul[2] <- 1L

rows <- c(ul[1], lr[1])
cols <- c(ul[2], lr[2])

if(!anyNA(rows)) stopifnot(rows[1] <= rows[2])
if(!anyNA(cols)) stopifnot(cols[1] <= cols[2])
if (!anyNA(rows)) stopifnot(rows[1] <= rows[2])
if (!anyNA(cols)) stopifnot(cols[1] <= cols[2])

structure(list(ul = ul, lr = lr),
structure(list(ul = ul, lr = lr, wsn = wsn),
class = c("cell_limits", "list"))

}
Expand All @@ -72,9 +79,10 @@ cell_limits <- function(ul = c(NA_integer_, NA_integer_),
print.cell_limits <- function(x, ...) {
ul <- ifelse(is.na(x$ul), "-", as.character(x$ul))
lr <- ifelse(is.na(x$lr), "-", as.character(x$lr))
wsn <- if (is.na(x$wsn)) "" else paste0(" in '", x$wsn, "'")

cat("<cell_limits (", ul[1], ", ", ul[2], ") x (",
lr[1], ", ", lr[2], ")>\n",
lr[1], ", ", lr[2], ")", wsn, ">\n",
sep = "")
}

Expand All @@ -101,37 +109,43 @@ as.cell_limits.NULL <- function(x) cell_limits()
#' as.cell_limits("A1:D8")
#' as.cell_limits("R5C11")
#' as.cell_limits("R2C3:R6C9")
#' as.cell_limits("Sheet1!R2C3:R6C9")
#' as.cell_limits("'Spaces are evil'!R2C3:R6C9")
#'
#' @export
as.cell_limits.character <- function(x) {

stopifnot(length(x) == 1L)
x_orig <- x

x <- rm_dollar_signs(x)

y <- unlist(strsplit(x, ":"))
stopifnot(length(y) %in% 1:2)
y <- unlist(strsplit(x, "!"))
wsn <- if (length(y) > 1) y[[1]] else NA_character_
wsn <- remove_single_quotes(wsn)
raw_rg <- y[[length(y)]]
raw_rg <- rm_dollar_signs(raw_rg)

y <- rep_len(y[!grepl("\\s+", y)], 2)
rg <- unlist(strsplit(raw_rg, ":"))
stopifnot(length(rg) %in% 1:2)
rg <- rep_len(rg[!grepl("\\s+", rg)], 2)

RC_regex <- "^R([0-9]+)C([0-9]+$)"
A1_regex <- "^[A-Za-z]{1,3}[0-9]+$"

if(all(grepl(A1_regex, y))) {
y <- A1_to_RC(y)
} else if(!all(grepl(RC_regex, y))) {
if (all(grepl(A1_regex, rg))) {
rg <- A1_to_RC(rg)
} else if (!all(grepl(RC_regex, rg))) {
stop("Trying to set cell limits, but requested range is invalid:\n",
x_orig)
x_orig)
}

m <- regexec("^R([0-9]+)C([0-9]+$)", y)
m2 <- regmatches(y, m)
m <- regexec("^R([0-9]+)C([0-9]+$)", rg)
m2 <- regmatches(rg, m)

jfun <- function(x) as.integer(x[2:3])
cell_limits(
jfun(m2[[1]]),
jfun(m2[[2]])
jfun(m2[[2]]),
wsn
)
}

Expand All @@ -140,6 +154,8 @@ as.cell_limits.character <- function(x) {
#'
#' @param x a cell_limits object
#' @param RC logical, requesting "R1C1" positioning notation
#' @param wsn logical, specifying whether worksheet name should be prepended to
#' the range, e.g. \code{Sheet1!A1:D4}
#'
#' @return length one character vector holding a cell range, in either A1 or
#' R1C1 positioning notation
Expand All @@ -149,19 +165,26 @@ as.cell_limits.character <- function(x) {
#' as.range(rgCL)
#' as.range(rgCL, RC = TRUE)
#'
#' rgCL_ws <- cell_limits(ul = c(1, 2), lr = c(7, 6), wsn = "A Sheet")
#' as.range(rgCL_ws)
#' as.range(rgCL_ws, RC = TRUE, wsn = TRUE)
#'
#' @export
as.range <- function(x, RC = FALSE) {
as.range <- function(x, RC = FALSE, wsn = FALSE) {

stopifnot(inherits(x, "cell_limits"))
stopifnot(inherits(x, "cell_limits"),
isTOGGLE(RC), isTOGGLE(wsn))

if(any(is.na(unlist(x)))) return(NA_character_)
if (anyNA(unlist(x[c("ul", "lr")]))) return(NA_character_)

range <- c(paste0("R", x$ul[1], "C", x$ul[2]),
paste0("R", x$lr[1], "C", x$lr[2]))

if(!RC) {
range <- c(row_col_to_RC(x$ul), row_col_to_RC(x$lr))
if (!RC) {
range <- RC_to_A1(range)
}
range <- paste(range, collapse = ":")

paste(range, collapse = ":")
if (wsn && !is.na(x$wsn)) {
range <- paste(add_single_quotes(x$wsn), range, sep = "!")
}
range
}
1 change: 1 addition & 0 deletions R/row-col-to-from-RC.R
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
row_col_to_RC <- function(x) paste0("R", x[1], "C", x[2])
10 changes: 9 additions & 1 deletion R/utils.R
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
rm_dollar_signs <- function(x) gsub('$', '', x, fixed = TRUE)

char0_to_NA <- function(x) if(length(x) < 1) NA_character_ else x
char0_to_NA <- function(x) if (length(x) < 1) NA_character_ else x

isTOGGLE <- function(x) is.null(x) || isTRUE(x) || identical(x, FALSE)

add_single_quotes <- function(x) {
if (grepl("\\s+", x)) {
x <- paste0("'", x, "'")
}
x
}

remove_single_quotes <- function(x) gsub("^'|'$", "", x)
3 changes: 2 additions & 1 deletion man/A1_to_RC.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion man/RC_to_A1.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions man/anchored.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 10 additions & 2 deletions man/as.range.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions man/cell_cols.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 14 additions & 3 deletions man/cell_limits.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 708d349

Please sign in to comment.