|
34 | 34 | #' [cast][theory-faq-coercion] to that type before transposing. |
35 | 35 | #' |
36 | 36 | #' Note that `null` can alter the output type, but cannot alter the output |
37 | | -#' size. |
| 37 | +#' size. See the examples for consequences of this. |
38 | 38 | #' |
39 | 39 | #' @param size The expected size of each element of `x`. If not provided, |
40 | 40 | #' computed automatically by [vec_size_common()]. |
|
71 | 71 | #' # Size 1 elements are recycled |
72 | 72 | #' list_transpose(list(1, 2:3, 4)) |
73 | 73 | #' |
| 74 | +#' # --------------------------------------------------------------------------- |
| 75 | +#' # Using `size` and `ptype` |
| 76 | +#' |
74 | 77 | #' # With size 0 elements, the invariants are a bit tricky! |
75 | 78 | #' # This must return a size 0 list, but then you lose expected |
76 | 79 | #' # type (integer) and size (2) information about the elements. |
|
89 | 92 | #' # To work around this, provide the lost `size` and `ptype` manually |
90 | 93 | #' list_transpose(out, size = vec_size(x), ptype = vec_ptype_common(!!!x)) |
91 | 94 | #' |
| 95 | +#' # --------------------------------------------------------------------------- |
| 96 | +#' # Padding |
| 97 | +#' |
92 | 98 | #' # If you'd like to pad with a missing value rather than erroring, |
93 | 99 | #' # you might do something like this, which left-pads |
94 | 100 | #' x <- list(1, 2:5, 6:7) |
|
97 | 103 | #' sizes <- list_sizes(x) |
98 | 104 | #' size <- max(sizes) |
99 | 105 | #' index <- which(sizes != size) |
| 106 | +#' |
100 | 107 | #' x[index] <- lapply( |
101 | 108 | #' index, |
102 | 109 | #' function(i) vec_c(rep(NA, times = size - sizes[[i]]), x[[i]]) |
103 | 110 | #' ) |
| 111 | +#' x |
| 112 | +#' |
104 | 113 | #' list_transpose(x) |
105 | 114 | #' |
| 115 | +#' # --------------------------------------------------------------------------- |
| 116 | +#' # `NULL` handling |
| 117 | +#' |
106 | 118 | #' # `NULL` values aren't allowed in `list_transpose()` |
107 | 119 | #' x <- list(1:3, NULL, 5:7, NULL) |
108 | 120 | #' try(list_transpose(x)) |
|
111 | 123 | #' list_transpose(x, null = NA) |
112 | 124 | #' list_transpose(x, null = -(1:3)) |
113 | 125 | #' |
114 | | -#' # Note that using `null` is not fully identical to swapping any `NULL`s |
115 | | -#' # with their replacement value ahead of the `list_transpose()` call. |
116 | | -#' # Most of the time `null` works as you'd expect, but some confusion can occur |
117 | | -#' # when you have a length >1 `null` and `x` is an empty list, a list of |
118 | | -#' # `NULL`s, or a list with only size 1 elements. The main thing to remember is |
119 | | -#' # that `null` is not allowed to change the output size, which makes it more |
120 | | -#' # predictable to program with, but sometimes requires you to provide more |
121 | | -#' # information through `size`. |
122 | | -#' |
123 | | -#' # This is an error, because the common size from the list is 0, |
124 | | -#' # and you can't recycle `null` to that size. |
125 | | -#' try(list_transpose(list(), null = 3:4)) |
126 | | -#' try(list_transpose(list(NULL), null = 3:4)) |
127 | | -#' |
128 | | -#' # This is also an error, because the common size from the list is 1, |
129 | | -#' # and you can't recycle `null` to that size either. |
130 | | -#' try(list_transpose(list(1, 2), null = 3:4)) |
131 | | -#' |
132 | | -#' # If you're programming with `list_transpose()` and you're supplying a |
133 | | -#' # length >1 `null` value like this, then that implies you know the |
134 | | -#' # expected element size (otherwise you wouldn't have been able to create |
135 | | -#' # the `null` value). Supply that `size` to override the inferred common size, |
136 | | -#' # and then things work as expected: |
137 | | -#' |
| 126 | +#' # When you don't know the list element size up front, but you still want |
| 127 | +#' # to replace `NULL`s with something, use a size 1 `null` value which will |
| 128 | +#' # get recycled to the element size after it has been computed |
| 129 | +#' list_transpose(list(), null = NA) |
| 130 | +#' list_transpose(list(1, NULL, 3), null = NA) |
| 131 | +#' list_transpose(list(1, NULL, 3:4), null = NA) |
| 132 | +#' |
| 133 | +#' # When you do know the list element size up front, it's best to also provide |
| 134 | +#' # that information as `size`, as this helps direct the recycling process |
| 135 | +#' # for `null`, particularly in the cases of an empty list, a list of `NULL`s, |
| 136 | +#' # or a list of size 1 elements. You typically know the list element size if |
| 137 | +#' # you are providing a `null` of size != 1, because otherwise you wouldn't |
| 138 | +#' # have been able to make `null` in the first place! |
| 139 | +#' size <- 2L |
| 140 | +#' null <- 3:4 |
| 141 | +#' |
| 142 | +#' # `size` overrides the inferred element size of 0 |
| 143 | +#' # |
| 144 | +#' # I: List size 0, Element size 0 |
| 145 | +#' # O: List size 0, Element size 0 |
| 146 | +#' try(list_transpose(list(), null = null)) |
138 | 147 | #' # I: List size 0, Element size 2 |
139 | 148 | #' # O: List size 2, Element size 0 |
140 | | -#' list_transpose(list(), null = 3:4, size = 2) |
| 149 | +#' list_transpose(list(), null = null, size = size) |
141 | 150 | #' |
| 151 | +#' # Same idea here |
| 152 | +#' # |
| 153 | +#' # I: List size 1, Element size 0 |
| 154 | +#' # O: List size 0, Element size 1 |
| 155 | +#' try(list_transpose(list(NULL), null = null)) |
142 | 156 | #' # I: List size 1, Element size 2 |
143 | 157 | #' # O: List size 2, Element size 1 |
144 | | -#' list_transpose(list(NULL), null = 3:4, size = 2) |
| 158 | +#' list_transpose(list(NULL), null = null, size = size) |
145 | 159 | #' |
| 160 | +#' # `size` overrides the inferred element size of 1 |
| 161 | +#' # |
| 162 | +#' # I: List size 2, Element size 1 |
| 163 | +#' # O: List size 1, Element size 2 |
| 164 | +#' try(list_transpose(list(1, 2), null = null)) |
146 | 165 | #' # I: List size 2, Element size 2 |
147 | 166 | #' # O: List size 2, Element size 2 |
148 | | -#' list_transpose(list(1, 2), null = 3:4, size = 2) |
| 167 | +#' list_transpose(list(1, 2), null = null, size = size) |
| 168 | +#' |
| 169 | +#' # The reason that `list_transpose()` recycles `null` to the common size |
| 170 | +#' # rather than letting `null` participate in common size determination is |
| 171 | +#' # due to this example. When supplying a size 1 `null`, most of the time |
| 172 | +#' # you don't know the element size, and you just want `null` to recycle to |
| 173 | +#' # whatever the required size will be. If `null` participated in determining |
| 174 | +#' # the common size, the output of this would be `list(logical())` rather than |
| 175 | +#' # `list()` because the element size would be computed as 1. Since a size 1 |
| 176 | +#' # `null` is much more common than a size !=1 `null`, we've optimized for this |
| 177 | +#' # case at the cost of needing to specify `size` explicitly in some scenarios. |
| 178 | +#' list_transpose(list(), null = NA) |
149 | 179 | list_transpose <- function( |
150 | 180 | x, |
151 | 181 | ..., |
|
0 commit comments