If the element(s) in the first list equal element(s) of the second list, replace with element(s) of the third list

55 Views Asked by At

I have three lists

Example Data

data <- list("A-B", "C-D", "E-F", "G-H", "I-J")
data_to_replace  <- list("A-B", "C-D")
replacement <- list("B-A", "D-C")
## Note: The length(data_to_replace)  and length(replacement ) are always equal. Which may range from 1 to 200

This is just a minimal example. I have several of these three, and the number varies on each list

Expected outcome

data_new <- list("B-A", "D-C", "E-F", "G-H", "I-J")

What I have tried

## function that reverses a string by words
reverse_words <- function(string) {
  ## split string by blank spaces
  string_split <- strsplit(as.character(string), split="-")
  ## How many split terms?
  string_length <- length(string_split[[1]])
  ## decide what to do
  if (string_length == 1) {
    ## one word (do nothing)
    reversed_string <- string_split[[1]]
  } else {
    ## more than one word (collapse them)
    reversed_split <- string_split[[1]][string_length:1]
    reversed_string <- paste(reversed_split, collapse="-")
  }
  ## output
  return(reversed_string)
} 

## Replacemnt
replacement <- lapply(data_to_replace, reverse_words)
data_new <- rapply(data, function(x) {
  replace(x, x == data_to_replace, replacement) ## This last line did not work
})
3

There are 3 best solutions below

0
Gregor Thomas On BEST ANSWER

Convert your inputs to vectors and then you can do this:

## direct replacement
data[data %in% data_to_replace] = replacement[match(data, data_to_replace)]

## or with `ifelse` if you want to save it as a new name 
## and keep `data` unmodified
ifelse(data %in% data_to_replace, replacement[match(data, data_to_replace)], data)
3
jay.sf On

This already existys in stringi::stri_replace_all_fixed.

> data[] <- stringi::stri_replace_all_fixed(data, data_to_replace, 
+                                           replacement, 
+                                           vectorize_all=FALSE)
> data
[[1]]
[1] "B-A"

[[2]]
[1] "D-C"

[[3]]
[1] "E-F"

[[4]]
[1] "G-H"

[[5]]
[1] "I-J"

Note, there's also a stringi::stri_replace_all_regex.

Maybe the reverse_words() function can be simplified.

> reverse_words2 <- \(x) paste(rev(el(strsplit(x, '-'))), collapse='-')
> (replacement <- lapply(data_to_replace, reverse_words2))
[[1]]
[1] "B-A"

[[2]]
[1] "D-C"
0
ThomasIsCoding On

In base R, you can simply use replace + match

> replace(data, match(data_to_replace, data), replacement)
[[1]]
[1] "B-A"

[[2]]
[1] "D-C"

[[3]]
[1] "E-F"

[[4]]
[1] "G-H"

[[5]]
[1] "I-J"