tidyverse/stringr how to find and replace on exact matches

Viewed 1416

I am trying to perform find and replace function. Prior sleuthing indicates this should be possible with stringr::str_replace and stringr::str_replace_all. However, I'm running into issues because partial matches in longer strings lead to unwanted results. I want the pattern argument to match the whole string not just portions of strings; when it is just a portion I want the replacement operation to ignore the cell. I would greatly appreciate any insight on how to accomplish exact match find and replace operations preferably in a mutate statement.

library(tidyverse)
df <- tribble(
  ~col1,
  "foo",
  "foo bar",
  "foo",
  "foo bar",
  "pizza"
)

Then to find and replace, what I see is commonly the following:

df %>% 
  mutate(col1 = str_replace_all(col1, pattern = "foo", replacement = "foo bar"))

Unfortunately, this produces unwanted results:

foo bar             
foo bar bar             
foo bar             
foo bar bar
pizza

I'm looking to get:

foo bar             
foo bar         
foo bar             
foo bar
pizza

Thanks in advance.

3 Answers

Specify the ^ and $ to suggest the start and end of the string so that it matches only the cases where 'foo' is the only word (Note that str_replace would also work fine as we are doing a single replacement here)

library(dplyr)
library(stringr)
df %>% 
   mutate(col1 = str_replace_all(col1, 
            pattern = "^foo$", replacement = "foo bar"))

-output

# A tibble: 4 x 1
  col1   
  <chr>  
1 foo bar
2 foo bar
3 foo bar
4 foo bar

Based on the example, we just need

df$col1 <- "foo bar"

Since you are doing an exact match instead of using regex you can use == for direct comparison.

library(dplyr)

df %>% mutate(col1 = replace(col1, col1 == 'foo', 'foo bar'))

#   col1   
#  <chr>  
#1 foo bar
#2 foo bar
#3 foo bar
#4 foo bar
#5 pizza  

Also you can do -

df$col1[df$col1 == 'foo'] <- 'foo bar'

Perhaps you need negative lookahead. Let me show you on an modified example-

library(tidyverse)
df <- tribble(
  ~col1,
  "foo pizza",
  "foo bar",
  "foo",
  "foo bar",
  "pizza"
)

df
#> # A tibble: 5 x 1
#>   col1     
#>   <chr>    
#> 1 foo pizza
#> 2 foo bar  
#> 3 foo      
#> 4 foo bar  
#> 5 pizza

df %>% mutate(col1 = str_replace(col1, 'foo(?!\\sbar)', 'foo bar'))
#> # A tibble: 5 x 1
#>   col1         
#>   <chr>        
#> 1 foo bar pizza
#> 2 foo bar      
#> 3 foo bar      
#> 4 foo bar      
#> 5 pizza

Created on 2021-06-26 by the reprex package (v2.0.0)

Related