Skip to content

Commit

Permalink
Polishing
Browse files Browse the repository at this point in the history
  • Loading branch information
hadley committed Aug 21, 2024
1 parent c732e0e commit 47ced5c
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 17 deletions.
28 changes: 17 additions & 11 deletions R/list-combine.R
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
#' same size (i.e. number of rows).
#' @param name_repair One of `"unique"`, `"universal"`, or `"check_unique"`.
#' See [vctrs::vec_as_names()] for the meaning of these options.
#' @param keep_empty An optional logical. If FALSE (the default), then the empty element is silently ignored;
#' if TRUE, then the empty element is kept as an NA`.
#' @param keep_empty An optional logical. If `FALSE` (the default), then
#' empty (`NULL`) elements are silently ignored; if `TRUE`, then empty
#' elements are preserved by converting to `NA`.
#' @inheritParams rlang::args_dots_empty
#' @export
#' @examples
Expand All @@ -33,20 +34,20 @@
#' x2 <- list(
#' a = data.frame(x = 1:2),
#' b = data.frame(y = "a"),
#' c = NULL)
#' c = NULL
#' )
#' list_rbind(x2)
#' list_rbind(x2, names_to = "id")
#' list_rbind(x2, names_to = "id", keep_empty = TRUE)
#' list_rbind(unname(x2), names_to = "id")
#' list_cbind(x2)
#' list_cbind(x2, keep_empty = TRUE)
#'
list_c <- function(x, ..., ptype = NULL, keep_empty = FALSE) {
vec_check_list(x)
check_dots_empty()

if(keep_empty) x <- convert_empty_element_to_NA(x)
if (keep_empty) {
x <- convert_null_to_NA(x)
}

# For `list_c()`, we don't expose `list_unchop()`'s `name_spec` arg,
# and instead strip outer names to avoid collisions with inner names
Expand All @@ -70,7 +71,9 @@ list_cbind <- function(
) {
check_list_of_data_frames(x)
check_dots_empty()
if(keep_empty) x <- convert_empty_element_to_NA(x)
if (keep_empty) {
x <- convert_null_to_NA(x)
}

vec_cbind(!!!x, .name_repair = name_repair, .size = size, .error_call = current_env())
}
Expand All @@ -80,7 +83,9 @@ list_cbind <- function(
list_rbind <- function(x, ..., names_to = rlang::zap(), ptype = NULL, keep_empty = FALSE) {
check_list_of_data_frames(x)
check_dots_empty()
if(keep_empty) x <- convert_empty_element_to_NA(x)
if (keep_empty) {
x <- convert_null_to_NA(x)
}

vec_rbind(!!!x, .names_to = names_to, .ptype = ptype, .error_call = current_env())
}
Expand All @@ -106,7 +111,8 @@ check_list_of_data_frames <- function(x, error_call = caller_env()) {
)
}

## used to convert empty elements into NA for list_binding functions
convert_empty_element_to_NA = function(x) {
map(x, function(x) if(vctrs::vec_is_empty(x)) NA else x)
convert_null_to_NA <- function(x) {
is_null <- map_lgl(x, is.null)
x[is_null] <- list(NA)
x
}
8 changes: 5 additions & 3 deletions man/list_c.Rd

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

15 changes: 12 additions & 3 deletions tests/testthat/test-list-combine.R
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,18 @@ test_that("NULLs are converted to NA when keep_empty = TRUE", {
df1 <- data.frame(x = 1)
df2 <- data.frame(y = 1)

expect_equal(list_c(list(1, NULL, 2), keep_empty = TRUE), c(1, NA, 2))
expect_equal(list_rbind(list(df1, NULL, df1), keep_empty = TRUE), data.frame(x = c(1, NA, 1)))
expect_equal(list_cbind(list(df1, z = NULL, df2), keep_empty = TRUE), data.frame(df1, z = NA, df2))
expect_equal(
list_c(list(1, NULL, 2), keep_empty = TRUE),
c(1, NA, 2)
)
expect_equal(
list_rbind(list(df1, NULL, df1), keep_empty = TRUE),
data.frame(x = c(1, NA, 1))
)
expect_equal(
list_cbind(list(df1, z = NULL, df2), keep_empty = TRUE),
data.frame(df1, z = NA, df2)
)
})

test_that("empty inputs return expected output", {
Expand Down

0 comments on commit 47ced5c

Please sign in to comment.