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