I have a strange issue with map. My goal is to return a list for every element. Conditional on the element I want a list of different length (or at least different logic applied to it). This seems to be very difficult and map is returning all kinds of values (see below):
map(1:2, ~ list(.x, .x 1))
# returns list(list(1,2), list(2,3)
map(1:2, ~ ifelse(.x > 1,
list(.x, .x 1),
list(.x, .x)))
# returns list(list(1), list(2))
map(1:2, ~ case_when(.x > 1 ~ list(.x, .x 1),
TRUE ~ list(.x)))
# returns list(list(1,1), list(2,3)
I found two methods to solve this:
# Add a list around it and then remove the list
map(1:2, ~ ifelse(.x > 1,
list(list(.x, .x 1)),
list(.x))) %>%
map(., ~.[[1]])
# Use a two step map_if
map_if(1:2, ~.x > 1,
~list(.x, .x 1)) %>%
map_if(is.integer, ~list(.x))
Both feel a little weird to me.. What is the best practice in this regard?
CodePudding user response:
We could use if/else
instead of ifelse
or case_when
as these require all arguments to be of same length (unless we rep
licate) i.e. the
testis of
length` 1
library(purrr)
map(1:2, ~ if(.x > 1) list(.x, .x 1) else list(.x, .x))
-output
[[1]]
[[1]][[1]]
[1] 1
[[1]][[2]]
[1] 1
[[2]]
[[2]][[1]]
[1] 2
[[2]][[2]]
[1] 3
NOTE: Here each element from the vector is looped and thus we don't need an ifelse
. If we want to use it anyway, do the ifelse
and wrap the output with the element looped in a list
map(1:2, ~ list(.x, ifelse(.x > 1,
.x 1,
.x)))
-output
[[1]]
[[1]][[1]]
[1] 1
[[1]][[2]]
[1] 1
[[2]]
[[2]][[1]]
[1] 2
[[2]][[2]]
[1] 3
CodePudding user response:
What about something like this:
library(tidyverse)
map(1:2, \(x) {test <- list(x, ` `(x > 1) x); test[!duplicated(test)]})
#> [[1]]
#> [[1]][[1]]
#> [1] 1
#>
#>
#> [[2]]
#> [[2]][[1]]
#> [1] 2
#>
#> [[2]][[2]]
#> [1] 3