按索引快速替换字符串中的多个字符

Replace multiple characters, by index, in a string quickly

我正在尝试用另一个字符快速替换字符串中的多个字符,例如 *

比如我有这样一个字符串:

string = "abcdefghij"

我还有一个索引向量,指示我想用另一个字符替换上面字符串中的字母的位置。

string_indexes_replaced = c(1, 4, 6, 9)

期望输出:

"*bc*e*gh*j"

我做了什么

我尝试了一种非常新手的方法,将字符拆分成一个列表,用 * 替换字符,然后将列表折叠回所需的字符串,如下所示:

library(dplyr)
library(stringi)

string%>%
strsplit(split = "")%>%
lapply(function(x) replace(x, string_indexes_replaced, rep("*", length(string_indexes_replaced))))%>%
lapply(stri_flatten)%>%
unlist(use.names = FALSE)

输出

"*bc*e*gh*j"

但很明显,应该有比我上面发布的更简单、更快的东西。有什么比我在这里演示的更简单、更快捷的吗?

我们可以使用substring

v1 <- c(1, 4, 6, 9)
for(i in seq_along(v1)) substring(string, v1[i], v1[i]) <- "*"
#[1] "*bc*e*gh*j"

由于我们使用的是stringi,另一个选项是

library(stringi)
stri_sub_all(string, from = v1, length = 1) <- "*"
string
#[1] "*bc*e*gh*j"

base R中,除了substring()for-loop的方法外,还可以用utf8ToInt()intToUtf8来成功

v <- utf8ToInt(string)
v[string_indexes_replaced ] <- utf8ToInt("*")
res <- intToUtf8(v)

这给出了

> res
[1] "*bc*e*gh*j"

一个简单的递归解决方案。时间效率应与迭代(for 循环)相同。好处是没有副作用(整数 ks 的赋值是本地化的),因此我们可以将其整个计算视为功能抽象,并将其提供给我们正在处理的更大程序的其他部分。这将有助于模块化代码。

# multi-replace for character vector input with length greater than 1
multi_replace_v <- function(v, r, ks) {
  ks <- as.integer(ks)
  if (length(ks) == 0) {
    v
  } else if (length(ks) == 1) {
    if (ks[[1]] > length(v) | ks[[1]] < 1) {
      stop("Invalid parameter: ks=", as.character(ks[[1]]), ". Valid range: 1-", as.character(length(v)))
    } else if (ks[[1]] == 1) {
      c(r, v[-1])
    } else if (ks[[1]] == length(v)) {
      c(v[-length(v)], r)
    } else {
      c(v[1:(ks[[1]]-1)], r, v[(ks[[1]]+1):length(v)])
      }
  } else {
    multi_replace_v(multi_replace_v(v, r, ks[[1]]), r, ks[-1])
  }
}

# multi-replace for input of single string character vector
multi_replace_s <- function(s, r, ks) paste0(multi_replace_v(unlist(strsplit(s, '')), r, ks), collapse = '') 

# multi-replace for both single string and long vector input
multi_replace <- function(v_or_s, r, ks) {
  if (length(v_or_s) == 1) {
    multi_replace_s(v_or_s, r, ks)
  } else if (length(v_or_s) > 1) {
    multi_replace_v(v_or_s, r, ks)
  } else {
    NULL
  }
}

# Example
> multi_replace('abcdefghij', "*", c(1,4,6,9))
[1] "*bc*e*gh*j"