`ifelse` function changes the type of object, how to fix it?

I use ifelse function to build a udf. But I find this function change the type of R object I want.
Here I want to use arg is_tibble to give the users options to get a tibble or not.
And I expect convert_tbl(a,is_tibble = T) returns a data.frame for you, but it fails.

library(tidyverse)
a <- 
    matrix(
        1:4
        ,nrow = 2
        ,byrow = T
    )
a
#>      [,1] [,2]
#> [1,]    1    2
#> [2,]    3    4
a %>% as_tibble()
#> # A tibble: 2 x 2
#>      V1    V2
#>   <int> <int>
#> 1     1     2
#> 2     3     4
convert_tbl <- 
    function(matrix,is_tibble=F){
        ifelse(
            is_tibble==F
            ,matrix
            ,as_tibble(matrix)
        )
    }
convert_tbl(a,is_tibble = T)        
#> [[1]]
#> [1] 1 3
convert_tbl(a,is_tibble = F)
#> [1] 1

Created on 2018-11-06 by the reprex package (v0.2.1)

Session info
devtools::session_info()
#> Session info -------------------------------------------------------------
#>  setting  value                                              
#>  version  R version 3.4.4 (2018-03-15)                       
#>  system   x86_64, mingw32                                    
#>  ui       RTerm                                              
#>  language (EN)                                               
#>  collate  Chinese (Simplified)_People's Republic of China.936
#>  tz       Asia/Taipei                                        
#>  date     2018-11-06
#> Packages -----------------------------------------------------------------
#>  package    * version    date       source                            
#>  assertthat   0.2.0      2017-04-11 CRAN (R 3.4.3)                    
#>  backports    1.1.2      2017-12-13 CRAN (R 3.4.3)                    
#>  base       * 3.4.4      2018-03-15 local                             
#>  bindr        0.1.1      2018-03-13 CRAN (R 3.4.4)                    
#>  bindrcpp     0.2.2      2018-03-29 CRAN (R 3.4.4)                    
#>  broom        0.5.0      2018-07-17 CRAN (R 3.4.4)                    
#>  cellranger   1.1.0      2016-07-27 CRAN (R 3.4.3)                    
#>  cli          1.0.0      2017-11-05 CRAN (R 3.4.3)                    
#>  colorspace   1.3-2      2016-12-14 CRAN (R 3.4.3)                    
#>  compiler     3.4.4      2018-03-15 local                             
#>  crayon       1.3.4      2017-09-16 CRAN (R 3.4.3)                    
#>  datasets   * 3.4.4      2018-03-15 local                             
#>  devtools     1.13.5     2018-02-18 CRAN (R 3.4.3)                    
#>  digest       0.6.18     2018-10-10 CRAN (R 3.4.4)                    
#>  dplyr      * 0.7.7      2018-10-16 CRAN (R 3.4.4)                    
#>  evaluate     0.10.1     2017-06-24 CRAN (R 3.4.3)                    
#>  fansi        0.2.3      2018-05-06 CRAN (R 3.4.4)                    
#>  forcats    * 0.3.0      2018-02-19 CRAN (R 3.4.3)                    
#>  ggplot2    * 3.1.0.9000 2018-11-02 Github (tidyverse/ggplot2@765f4de)
#>  glue         1.3.0      2018-07-17 CRAN (R 3.4.4)                    
#>  graphics   * 3.4.4      2018-03-15 local                             
#>  grDevices  * 3.4.4      2018-03-15 local                             
#>  grid         3.4.4      2018-03-15 local                             
#>  gtable       0.2.0      2016-02-26 CRAN (R 3.4.3)                    
#>  haven        1.1.1      2018-01-18 CRAN (R 3.4.3)                    
#>  hms          0.4.2      2018-03-10 CRAN (R 3.4.4)                    
#>  htmltools    0.3.6      2017-04-28 CRAN (R 3.4.3)                    
#>  httr         1.3.1      2017-08-20 CRAN (R 3.4.3)                    
#>  jsonlite     1.5        2017-06-01 CRAN (R 3.4.3)                    
#>  knitr        1.20       2018-02-20 CRAN (R 3.4.3)                    
#>  lattice      0.20-35    2017-03-25 CRAN (R 3.4.4)                    
#>  lazyeval     0.2.1      2017-10-29 CRAN (R 3.4.3)                    
#>  lubridate    1.7.4      2018-04-11 CRAN (R 3.4.4)                    
#>  magrittr     1.5        2014-11-22 CRAN (R 3.4.3)                    
#>  memoise      1.1.0      2017-04-21 CRAN (R 3.4.4)                    
#>  methods    * 3.4.4      2018-03-15 local                             
#>  modelr       0.1.2      2018-05-11 CRAN (R 3.4.4)                    
#>  munsell      0.5.0      2018-06-12 CRAN (R 3.4.4)                    
#>  nlme         3.1-137    2018-04-07 CRAN (R 3.4.4)                    
#>  pillar       1.3.0.9000 2018-07-30 Github (r-lib/pillar@7582a75)     
#>  pkgconfig    2.0.1      2017-03-21 CRAN (R 3.4.3)                    
#>  plyr         1.8.4      2016-06-08 CRAN (R 3.4.3)                    
#>  purrr      * 0.2.5      2018-05-29 CRAN (R 3.4.4)                    
#>  R6           2.3.0      2018-10-04 CRAN (R 3.4.4)                    
#>  Rcpp         0.12.18    2018-07-23 CRAN (R 3.4.4)                    
#>  readr      * 1.1.1      2017-05-16 CRAN (R 3.4.3)                    
#>  readxl       1.1.0      2018-04-20 CRAN (R 3.4.4)                    
#>  rlang        0.3.0.1    2018-10-25 CRAN (R 3.4.4)                    
#>  rmarkdown    1.10       2018-06-11 CRAN (R 3.4.4)                    
#>  rprojroot    1.3-2      2018-01-03 CRAN (R 3.4.3)                    
#>  rvest        0.3.2      2016-06-17 CRAN (R 3.4.3)                    
#>  scales       1.0.0      2018-08-09 CRAN (R 3.4.4)                    
#>  stats      * 3.4.4      2018-03-15 local                             
#>  stringi      1.1.7      2018-03-12 CRAN (R 3.4.4)                    
#>  stringr    * 1.3.1      2018-05-10 CRAN (R 3.4.4)                    
#>  tibble     * 1.4.2      2018-01-22 CRAN (R 3.4.3)                    
#>  tidyr      * 0.8.0      2018-01-29 CRAN (R 3.4.3)                    
#>  tidyselect   0.2.4      2018-02-26 CRAN (R 3.4.3)                    
#>  tidyverse  * 1.2.1      2017-11-14 CRAN (R 3.4.4)                    
#>  tools        3.4.4      2018-03-15 local                             
#>  utf8         1.1.4      2018-05-24 CRAN (R 3.4.4)                    
#>  utils      * 3.4.4      2018-03-15 local                             
#>  withr        2.1.2      2018-03-15 CRAN (R 3.4.4)                    
#>  xml2         1.2.0      2018-01-24 CRAN (R 3.4.3)                    
#>  yaml         2.2.0      2018-07-25 CRAN (R 3.4.4)

Why not use if... else instead? Documentation on ifelse says that "ifelse returns a value with the same shape as test" (emphasis mine), so it's working as intended from it's point of view. if... else doesn't have this problem:

library(tidyverse)
a <- 
  matrix(
    1:4
    ,nrow = 2
    ,byrow = TRUE
  )
a
#>      [,1] [,2]
#> [1,]    1    2
#> [2,]    3    4
a %>% as_tibble()
#> # A tibble: 2 x 2
#>      V1    V2
#>   <int> <int>
#> 1     1     2
#> 2     3     4

convert_tbl <- function(matrix,is_tibble=FALSE){
  if(is_tibble)
    as_tibble(matrix)
  else
    matrix
}
convert_tbl(a, is_tibble = TRUE)        
#> # A tibble: 2 x 2
#>      V1    V2
#>   <int> <int>
#> 1     1     2
#> 2     3     4
convert_tbl(a, is_tibble = FALSE)
#>      [,1] [,2]
#> [1,]    1    2
#> [2,]    3    4

Created on 2018-11-06 by the reprex package (v0.2.1)

And not connected to your question, but couple of suggestions on coding style:

  1. It is better to use TRUE/FALSE, not T/F. Code is read more often than written, so spare some thought to people who will read it later and seeing TRUE/FALSE is simply easier.
  2. is_*** usually indicates a predicate, meaning function that itself will return TRUE/FALSE and not something that is TRUE/FALSE to begin with. The way you use it in your function is probably more like tibblify or convert_to_tibble or something like that.
4 Likes

Small addition, looks like your problem with how ifelse works is annoying for not only you :slight_smile:. There is new package from Hadley that, apparently, was motivated in part by this inconsistent behavior

https://vctrs.r-lib.org/articles/stability.html#ifelse

3 Likes

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.