David T
David T

Reputation: 2143

Why can't case_when return different-length vectors?

This fails:

library(tidyverse)
myFn <- function(nmbr){
  case_when(
    nmbr > 3 ~ letters[1:3],
    TRUE ~ letters[1:2]
  )
}

myFn(4)
# Error: `TRUE ~ letters[1:2]` must be length 3 or one, not 2
# Run `rlang::last_error()` to see where the error occurred.

Why does it fail? Why is case_when built in such a way that its branches can't return different-length vectors? I'd like myFn to work so that I can do things like:

tibble(fruit = c("apple", "grape"),
       count = 3:4) %>% 
  mutate(bowl = myFn(count)) %>% 
  unnest(col = "bowl")

and get

# A tibble: 5 x 3
fruit count  bowl
<chr> <int> <int>
1 apple     3     a
2 apple     3     b
3 grape     4     a
4 grape     4     b
5 grape     4     c

I can get it to work - by writing a non-vectorized myFn using if/else, then wrapping it in map, but why should I have to?

Upvotes: 5

Views: 1392

Answers (2)

lifedroid
lifedroid

Reputation: 194

Extending r2Evans answer, you can unlist prior to returning. Here is my code that I am using as part of my nesting algorithm.

test_fn <- function(StockCode) {
  L <- dplyr::case_when(stringr::str_starts(StockCode, "A") ~ list(7500),
                        stringr::str_starts(StockCode, "FL") ~ list(6000),
                        stringr::str_starts(StockCode, "PIPE") ~ list(6500),
                        stringr::str_starts(StockCode, "RHS") ~ 
                          list(c(8000, 12000)),
                        stringr::str_starts(StockCode, "RND") ~ list(6000),
                        stringr::str_starts(StockCode, "SQ") ~ list(6000),
                        TRUE ~
                          list(c(9000, 10500, 12000, 13500, 15000, 16500, 18000))) %>%
    unlist()
  return(L)
}

test_fn("RHS")
test_fn("CH")

The output will be the vector assigned to the list.

Upvotes: 0

r2evans
r2evans

Reputation: 160437

Per my comments, your function needs to return one element for each row of input. However, each of those elements can be a list of length 0 or more (and arbitrary complexity). Try this:

myFn <- function(nmbr){
  case_when(
    nmbr > 3 ~ list(letters[1:3]),
    TRUE ~ list(letters[1:2])
  )
}
tibble(fruit = c("apple", "grape"),
       count = 3:4) %>%
  mutate(bowl = myFn(count))
# # A tibble: 2 x 3
#   fruit count bowl     
#   <chr> <int> <list>   
# 1 apple     3 <chr [2]>
# 2 grape     4 <chr [3]>
tibble(fruit = c("apple", "grape"),
       count = 3:4) %>%
  mutate(bowl = myFn(count)) %>%
  unnest(col = "bowl")
# # A tibble: 5 x 3
#   fruit count bowl 
#   <chr> <int> <chr>
# 1 apple     3 a    
# 2 apple     3 b    
# 3 grape     4 a    
# 4 grape     4 b    
# 5 grape     4 c    

Upvotes: 5

Related Questions