Search code examples
rdplyrtidyr

cbind within a conditional ifelse statement


I have several data frames that are generated by a script, and sometimes they are empty. At the end of my procedure I would like to use cbind to put all of the data frames together, however this of course fails if the data frame is "empty."

df1 <- structure(list(id1 = c("A", "A", "B", "B", "A", "A"), 
                      val1 = c(4,2, 1, 5, 3, 4), 
                      id2 = c("C", "B", "C", "C", "B", NA), 
                      val2 = c(2, 3, 2, 2, 3, NA)), 
                 row.names = c(NA, 6L), class = "data.frame")

df2 <- structure(list(), names = character(0), 
                 row.names = integer(0), 
                 class = c("tbl_df", "tbl", "data.frame"))

df3 <- structure(list(id3 = c("X", "X", "X", "Y", "Y", "Y"), 
                      val3 = c(8, 10, 11, 9, 22, 1)), 
                 class = "data.frame", row.names = c(NA, -6L))

I have tried to do this with a nested ifelse statement. For example:

df_new <- ifelse(nrow(df1)==0, cbind(df2, df3),
                 ifelse(nrow(df2)==0, cbind(df1, df3), 
                        cbind(df1, df2, df3)))

However, this just returns the first column of df1 as a list... Is there a better way to achieve this? Or is there something about ifelse or cbind that I'm not understanding? Thank you in advance!


Solution

  • You can put your data.frames in a list, remove the empty elements (length == 0), and then use cbind in a do.call function.

    l <- list(df1, df2, df3)
    do.call("cbind", l[lengths(l) > 0])
    
    #   id1 val1  id2 val2 id3 val3
    # 1   A    4    C    2   X    8
    # 2   A    2    B    3   X   10
    # 3   B    1    C    2   X   11
    # 4   B    5    C    2   Y    9
    # 5   A    3    B    3   Y   22
    # 6   A    4 <NA>   NA   Y    1
    

    If you prefer dplyr functions, you can use dplyr::bind_cols:

    dplyr::bind_cols(l[lengths(l) > 0])
    

    Also purrr has a convenient function that you can use to remove empty elements, compact:

    dplyr::bind_cols(purrr::compact(l))