1#' Combine many vectors into one vector 2#' 3#' Combine all arguments into a new vector of common type. 4#' 5#' @section Invariants: 6#' * `vec_size(vec_c(x, y)) == vec_size(x) + vec_size(y)` 7#' * `vec_ptype(vec_c(x, y)) == vec_ptype_common(x, y)`. 8#' 9#' @section Dependencies: 10#' 11#' ## vctrs dependencies 12#' 13#' - [vec_cast_common()] with fallback 14#' - [vec_proxy()] 15#' - [vec_restore()] 16#' 17#' 18#' ## base dependencies 19#' 20#' - [base::c()] 21#' 22#' If inputs inherit from a common class hierarchy, `vec_c()` falls 23#' back to `base::c()` if there exists a `c()` method implemented for 24#' this class hierarchy. 25#' 26#' @param ... Vectors to coerce. 27#' @param .name_repair How to repair names, see `repair` options in 28#' [vec_as_names()]. 29#' @return A vector with class given by `.ptype`, and length equal to the 30#' sum of the `vec_size()` of the contents of `...`. 31#' 32#' The vector will have names if the individual components have names 33#' (inner names) or if the arguments are named (outer names). If both 34#' inner and outer names are present, an error is thrown unless a 35#' `.name_spec` is provided. 36#' @inheritParams vec_ptype_show 37#' @inheritParams name_spec 38#' @inheritParams vec_as_names 39#' @seealso [vec_cbind()]/[vec_rbind()] for combining data frames by rows 40#' or columns. 41#' @export 42#' @examples 43#' vec_c(FALSE, 1L, 1.5) 44#' 45#' # Date/times -------------------------- 46#' c(Sys.Date(), Sys.time()) 47#' c(Sys.time(), Sys.Date()) 48#' 49#' vec_c(Sys.Date(), Sys.time()) 50#' vec_c(Sys.time(), Sys.Date()) 51#' 52#' # Factors ----------------------------- 53#' c(factor("a"), factor("b")) 54#' vec_c(factor("a"), factor("b")) 55#' 56#' 57#' # By default, named inputs must be length 1: 58#' vec_c(name = 1) 59#' try(vec_c(name = 1:3)) 60#' 61#' # Pass a name specification to work around this: 62#' vec_c(name = 1:3, .name_spec = "{outer}_{inner}") 63#' 64#' # See `?name_spec` for more examples of name specifications. 65vec_c <- function(..., 66 .ptype = NULL, 67 .name_spec = NULL, 68 .name_repair = c("minimal", "unique", "check_unique", "universal")) { 69 .External2(vctrs_c, .ptype, .name_spec, .name_repair) 70} 71vec_c <- fn_inline_formals(vec_c, ".name_repair") 72 73base_c <- function(xs) { 74 # Dispatch in the base namespace which inherits from the global env 75 exec("c", !!!xs, .env = ns_env("base")) 76} 77 78base_c_invoke <- function(xs) { 79 # Remove all `NULL` arguments which prevent dispatch if in first 80 # position and might not be handled correctly by methods 81 xs <- compact(xs) 82 83 unspecified <- map_lgl(xs, fallback_is_unspecified) 84 if (!any(unspecified)) { 85 return(base_c(xs)) 86 } 87 88 # First concatenate without the unspecified chunks. This way the 89 # `c()` method doesn't need to handle unspecified inputs correctly, 90 # and we're guaranteed to dispatch on the correct class even if the 91 # first input is unspecified. 92 out <- base_c(xs[!unspecified]) 93 94 # Create index vector with `NA` locations for unspecified chunks 95 locs <- c_locs(xs) 96 locs[unspecified] <- map(locs[unspecified], rep_along, na_int) 97 locs[!unspecified] <- c_locs(xs[!unspecified]) 98 locs <- vec_c(!!!locs, .ptype = int()) 99 100 # Expand the concatenated vector with unspecified chunks 101 out[locs] 102} 103 104# FIXME: Should be unnecessary in the future. We currently attach an 105# attribute to unspecified columns initialised in `df_cast()`. We 106# can't use an unspecified vector because we (unnecessarily but for 107# convenience) go through `vec_assign()` before falling back in 108# `vec_rbind()`. 109fallback_is_unspecified <- function(x) { 110 is_unspecified(x) || is_true(attr(x, "vctrs:::unspecified")) 111} 112 113c_locs <- function(xs) { 114 locs <- reduce(lengths(xs), .init = list(0), function(output, input) { 115 n <- last(last(output)) 116 c(output, list(seq(n + 1, n + input))) 117 }) 118 119 locs[-1] 120} 121