'Conditional mutate - creating a new variable with coalesce

I'm scraping data from a website and depending on the structure of the page. I have an inner join in my final table that either joins clean on WON and LOST variables or I need to perform a clean-up step to coalesce four variables into the WON and LOST.

My issue is that my code, as written, always assumes a dirty join (bolded mutates).

Error in `mutate()`:
! Problem while computing `WON.x = coalesce(WON.x, WON.y)`.
Caused by error in `list2()`:
! object 'WON.x' not found

I believe the solution is that I need the two mutates to only run if there's a dirty join (i.e., WON and LOST become WON.x, WON.y, LOST.x, LOST.y).

I cannot figure this out and have tried various approaches using "matched", "across(any_of)" etc, but I cannot get it work. I think I'm getting mixed up in the syntax trying to include coalesce.

Any help appreciated in how to mutate and coalesce like this only if certain other columns exist. Thanks.

final=inner_join(temp1,temp2, by="id") %>% 
    select(-any_of(c("DRAW.x", "DRAW.y"))) %>% 
    **mutate(WON.x = coalesce(WON.x,WON.y)) %>% 
    mutate(LOST.x =coalesce(LOST.x,LOST.y)) %>%** 
    select(-any_of(c("WON.y","LOST.y"))) %>% 
    rename(WON=WON.x, LOST=LOST.x)```


Solution 1:[1]

Not everything should be squeezed into a pipe. I would do the following:

library(tidyverse)

## These two give a clean join:
temp1 <- tibble(id = 1:3,
                 WON= 5:7)
temp2 <- tibble(id = 1:3,
                 LOST = 3:5)

## These two give a dirty join:
temp1 <- tibble(id = 1:3,
                 WON = 5:7,
                 LOST = c(3L, NA, 5L))
temp2 <- tibble(id = 1:3,
                WON = c(NA, NA, 7L),
                LOST = 3:5)

final <- inner_join(temp1, temp2, by = "id")

if("WON.y" %in% names(final)){
    final <- final %>% 
        mutate(WON = coalesce(WON.x, WON.y),
               LOST = coalesce(LOST.x, LOST.y),
               .keep = "unused")
}
final

I honestly can't think of a clear way to do it directly in a pipe. It is not a good idea to write an abstruse one-liner just to make it fit into a pipe. Clear code is best even if you must interrupt a pipe.

If you insist, you could write a little function that becomes pipeable. But then the reader must go off and find this other code that also isn't really in the pipe.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Michael Dewar