Here are some alternatives
1) gsubfn gsubfn
is a generalization of gsub
in which the second argument can not only be a character string but alternately a named list which we use here (or a function or proto object).
library(gsubfn)
gsubfn("^.*$", setNames(as.list(change), tochange), cn)
## [1] "I" "A" "B" "C" "A" "C" "D" "P"
2) Reduce A base R solution is to use Reduce
dict <- setNames(change, tochange)
Reduce(\(x, y) replace(x, names(y), y), init = cn, dict)
## [1] "I" "A" "B" "C" "A" "C" "D" "P"
3) chartr If the names in the strings are single characters, as in the question, then base R's chartr
can be used
chartr(paste0(tochange, collapse = ""), paste0(change, collapse = ""), cn)
## [1] "I" "A" "B" "C" "A" "C" "D" "P"
or hard coding the names
chartr("XY", "AB", cn)
## [1] "I" "A" "B" "C" "A" "C" "D" "P"
Circularity
Although it seems unlikely that the problem here would exhibit circularity such as in where A -> B -> A we can test for it if you think it is possible.
library(igraph)
cnt <- cbind(change, tochange) |>
graph_from_edgelist() |>
count_components()
if (cnt != length(change)) stop("circularity found")
Note
Inputs used
cn <- c("I", "A", "B", "C", "A", "C", "D", "P")
change <- c("A", "B")
tochange <- c("X", "Y")
"A" --> "B"
and"B" --> "A"
? Seems it cannot give the desired mapping – Fifteen