diff --git a/NEWS.md b/NEWS.md index 648fa417..c7db769b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,6 +3,7 @@ # openxlsx 4.2.8.1 * Fix for upcoming `testthat` release (@hadley, [#530](https://github.com/ycphs/openxlsx/pull/530)) +* Add overwrite support to writeData() to clear stale cells when rewriting with a smaller data range ([#536](https://github.com/ycphs/openxlsx/pull/536)) # openxlsx 4.2.8 diff --git a/R/writeData.R b/R/writeData.R index 1ac1ab26..09822429 100644 --- a/R/writeData.R +++ b/R/writeData.R @@ -45,6 +45,7 @@ #' @param na.string If not NULL, and if `keepNA` is `TRUE`, NA values are converted to this string in Excel. #' @param name If not NULL, a named region is defined. #' @param sep Only applies to list columns. The separator used to collapse list columns to a character vector e.g. sapply(x$list_column, paste, collapse = sep). +#' @param overwrite If `TRUE`, remove all existing cell values from the entire target worksheet before writing `x` (not just the cells in the target write range). #' @seealso [writeDataTable()] #' @export writeData #' @details Formulae written using writeFormula to a Workbook object will not get picked up by read.xlsx(). @@ -176,7 +177,8 @@ writeData <- function( name = NULL, sep = ", ", col.names, - row.names + row.names, + overwrite = FALSE ) { x <- force(x) @@ -224,6 +226,7 @@ writeData <- function( assert_class(wb, "Workbook") assert_true_false(colNames) assert_true_false(rowNames) + assert_true_false(overwrite) assert_character1(sep) assert_class(headerStyle, "Style", or_null = TRUE) @@ -305,6 +308,17 @@ writeData <- function( error_msg = "Cannot overwrite table headers. Avoid writing over the header row or see getTables() & removeTables() to remove the table object." ) + if (overwrite) { + sheet_data <- wb$worksheets[[sheetX]]$sheet_data + if (sheet_data$n_elements > 0) { + sheet_data$delete( + rows_in = sheet_data$rows, + cols_in = sheet_data$cols, + grid_expand = FALSE + ) + } + } + ## write autoFilter, can only have a single filter per worksheet if (withFilter) { coords <- data.frame( diff --git a/man/writeData.Rd b/man/writeData.Rd index be986f0d..7d552476 100644 --- a/man/writeData.Rd +++ b/man/writeData.Rd @@ -24,7 +24,8 @@ writeData( name = NULL, sep = ", ", col.names, - row.names + row.names, + overwrite = FALSE ) } \arguments{ @@ -88,6 +89,8 @@ each column. If "\code{all}" all cell borders are drawn.} \item{sep}{Only applies to list columns. The separator used to collapse list columns to a character vector e.g. sapply(x$list_column, paste, collapse = sep).} \item{row.names, col.names}{Deprecated, please use \code{rowNames}, \code{colNames} instead} + +\item{overwrite}{If \code{TRUE}, remove all existing cell values from the entire target worksheet before writing \code{x} (not just the cells in the target write range).} } \value{ invisible(0) diff --git a/tests/testthat/test-writeData.R b/tests/testthat/test-writeData.R index 43b3dadc..aa0a823d 100644 --- a/tests/testthat/test-writeData.R +++ b/tests/testthat/test-writeData.R @@ -67,3 +67,35 @@ test_that("as.character.formula() works [312]", { expect_identical(before, middle, ignore.environment = TRUE) expect_identical(before, end, ignore.environment = TRUE) }) + + +test_that("writeData(overwrite = TRUE) works (#536)", { + wb <- createWorkbook() + addWorksheet(wb, "sheet") + + old <- data.frame(a = 1:6, b = letters[1:6], stringsAsFactors = FALSE) + new <- old[1:4, , drop = FALSE] + + writeData(wb, "sheet", old) + writeData(wb, "sheet", new) + out_no_overwrite <- readWorkbook(wb, "sheet") + expect_equal(nrow(out_no_overwrite), 6) + + writeData(wb, "sheet", new, overwrite = TRUE) + out_overwrite <- readWorkbook(wb, "sheet") + expect_equal(out_overwrite, new) + + wb2 <- createWorkbook() + addWorksheet(wb2, "sheet") + tab_df <- data.frame(x = 1:2) + writeDataTable(wb2, "sheet", tab_df) + before <- readWorkbook(wb2, "sheet") + + expect_error( + writeData(wb2, "sheet", data.frame(y = 1), overwrite = TRUE), + "Cannot overwrite table headers" + ) + + after <- readWorkbook(wb2, "sheet") + expect_equal(after, before) +})