Быстрая замена нескольких символов в строке по индексу

Я пытаюсь быстро заменить несколько символов в строке другим символом, например *

Например, у меня есть такая строка, как:

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"

но ясно, что должно быть что-то более простое и быстрое, чем то, что я опубликовал выше. Есть ли что-нибудь проще и быстрее, чем то, что я здесь продемонстрировал?


person InfiniteFlash    schedule 28.11.2019    source источник


Ответы (3)


в base R, помимо метода substring() и for-loop, показанного @akrun,, вы можете использовать utf8ToInt() и intToUtf8, чтобы сделать это

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

который дает

> res
[1] "*bc*e*gh*j"
person ThomasIsCoding    schedule 28.11.2019
comment
@ThomasIsCoding Спасибо. Я отмечу это как принятый ответ завтра, если до тех пор не появится еще более быстрое решение. - person InfiniteFlash; 29.11.2019

Мы можем использовать 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"
person akrun    schedule 28.11.2019
comment
Спасибо @akrun. Я собираюсь немного подождать, прежде чем принять это как ответ, потому что я хочу увидеть, есть ли у кого-нибудь другие подходы, которые хорошо масштабируются по времени для более длинных строк, и было ли это применено к списку строк. - person InfiniteFlash; 28.11.2019
comment
@InfiniteFlashChess Хорошо. Я добавил еще один вариант от stringi - person akrun; 28.11.2019
comment
Большой. Спасибо за ваши усилия и время. Вы отличный ресурс для сообщества. - person InfiniteFlash; 28.11.2019
comment
Ваша строка stri_sub_all() великолепна, но немного медленнее, чем у Томаса, после того, как я провел несколько тестов для моей задачи. - person InfiniteFlash; 29.11.2019

Простое рекурсивное решение. Эффективность по времени должна быть такой же, как и у итерации (для цикла). Преимущество заключается в отсутствии побочного эффекта (присвоение целого числа 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"
person englealuze    schedule 25.05.2020