Skip to content

Commit 536209a

Browse files
committed
temp
1 parent 1d7c091 commit 536209a

File tree

3 files changed

+145
-62
lines changed

3 files changed

+145
-62
lines changed

R/list-transpose.R

Lines changed: 58 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
#' [cast][theory-faq-coercion] to that type before transposing.
3535
#'
3636
#' Note that `null` can alter the output type, but cannot alter the output
37-
#' size.
37+
#' size. See the examples for consequences of this.
3838
#'
3939
#' @param size The expected size of each element of `x`. If not provided,
4040
#' computed automatically by [vec_size_common()].
@@ -71,6 +71,9 @@
7171
#' # Size 1 elements are recycled
7272
#' list_transpose(list(1, 2:3, 4))
7373
#'
74+
#' # ---------------------------------------------------------------------------
75+
#' # Using `size` and `ptype`
76+
#'
7477
#' # With size 0 elements, the invariants are a bit tricky!
7578
#' # This must return a size 0 list, but then you lose expected
7679
#' # type (integer) and size (2) information about the elements.
@@ -89,6 +92,9 @@
8992
#' # To work around this, provide the lost `size` and `ptype` manually
9093
#' list_transpose(out, size = vec_size(x), ptype = vec_ptype_common(!!!x))
9194
#'
95+
#' # ---------------------------------------------------------------------------
96+
#' # Padding
97+
#'
9298
#' # If you'd like to pad with a missing value rather than erroring,
9399
#' # you might do something like this, which left-pads
94100
#' x <- list(1, 2:5, 6:7)
@@ -97,12 +103,18 @@
97103
#' sizes <- list_sizes(x)
98104
#' size <- max(sizes)
99105
#' index <- which(sizes != size)
106+
#'
100107
#' x[index] <- lapply(
101108
#' index,
102109
#' function(i) vec_c(rep(NA, times = size - sizes[[i]]), x[[i]])
103110
#' )
111+
#' x
112+
#'
104113
#' list_transpose(x)
105114
#'
115+
#' # ---------------------------------------------------------------------------
116+
#' # `NULL` handling
117+
#'
106118
#' # `NULL` values aren't allowed in `list_transpose()`
107119
#' x <- list(1:3, NULL, 5:7, NULL)
108120
#' try(list_transpose(x))
@@ -111,41 +123,59 @@
111123
#' list_transpose(x, null = NA)
112124
#' list_transpose(x, null = -(1:3))
113125
#'
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))
138147
#' # I: List size 0, Element size 2
139148
#' # O: List size 2, Element size 0
140-
#' list_transpose(list(), null = 3:4, size = 2)
149+
#' list_transpose(list(), null = null, size = size)
141150
#'
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))
142156
#' # I: List size 1, Element size 2
143157
#' # 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)
145159
#'
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))
146165
#' # I: List size 2, Element size 2
147166
#' # 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)
149179
list_transpose <- function(
150180
x,
151181
...,

man/list_transpose.Rd

Lines changed: 58 additions & 28 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-list-transpose.R

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -347,8 +347,13 @@ test_that("`null` size 0 behavior", {
347347
})
348348

349349
test_that("`null` size 1 behavior", {
350-
# This is the easy to explain case because everything recycles as you'd
351-
# imagine it to work anyways
350+
# This example of `list_transpose(list(), null = 3)` is a big reason why
351+
# we recycle `null` rather than letting it participate in common size
352+
# determination. A size 1 `null` is a very common way to say "I don't know
353+
# what the element size is, but replace `NULL` with this and recycle it.".
354+
# Since the user has no preexisting knowledge about the element size, the
355+
# size of `null` should not impact the output, and you should get `list()`,
356+
# not `list(numeric())`.
352357

353358
# Element common size is inferred to be 0 from `x`
354359
#
@@ -379,6 +384,24 @@ test_that("`null` size 1 behavior", {
379384
list_transpose(list(1, 2, NULL), null = 3),
380385
list(c(1, 2, 3))
381386
)
387+
388+
# Like with `null` size 0 and size >1, you can still supply `size` to override
389+
# the inferred size if you know the element size is actually 1
390+
size <- 1L
391+
null <- 3
392+
393+
# I: List size 0, Element size 1
394+
# O: List size 1, Element size 0
395+
expect_identical(
396+
list_transpose(list(), null = null, size = size),
397+
list(numeric())
398+
)
399+
# I: List size 1, Element size 1
400+
# O: List size 1, Element size 1
401+
expect_identical(
402+
list_transpose(list(NULL), null = null, size = size),
403+
list(3)
404+
)
382405
})
383406

384407
test_that("`null` size >1 behavior", {
@@ -420,26 +443,26 @@ test_that("`null` size >1 behavior", {
420443
# I: List size 0, Element size 2
421444
# O: List size 2, Element size 0
422445
expect_identical(
423-
list_transpose(list(), null = null, size = 2),
446+
list_transpose(list(), null = null, size = size),
424447
list(double(), double())
425448
)
426449
# I: List size 1, Element size 2
427450
# O: List size 2, Element size 1
428451
expect_identical(
429-
list_transpose(list(NULL), null = null, size = 2),
452+
list_transpose(list(NULL), null = null, size = size),
430453
list(3, 4)
431454
)
432455

433456
# I: List size 2, Element size 2
434457
# O: List size 2, Element size 2
435458
expect_identical(
436-
list_transpose(list(1, 2), null = null, size = 2),
459+
list_transpose(list(1, 2), null = null, size = size),
437460
list(c(1, 2), c(1, 2))
438461
)
439462
# I: List size 3, Element size 2
440463
# O: List size 2, Element size 3
441464
expect_identical(
442-
list_transpose(list(1, 2, NULL), null = null, size = 2),
465+
list_transpose(list(1, 2, NULL), null = null, size = size),
443466
list(c(1, 2, 3), c(1, 2, 4))
444467
)
445468
})

0 commit comments

Comments
 (0)