【问题标题】:Remove duplicates within consecutive runs of characters删除连续字符中的重复项
【发布时间】:2021-02-24 16:52:35
【问题描述】:

我的字符串包含很多重复项,如下所示:

tst <- c("C>C>C>B>B>B>B>C>C>*>*>*>*>*>C", "A>A>A", "*>B>B", 
     "A>A>A>A>A>*>A>A>A>*>*>*>*>A>A", "*>C>C", "A")

我想删除所有连续重复的大写和“*”字符,所以预期的结果是这样的:

[1] "CBC*C" "A"     "*B"    "A*A*A" "*C"    "A"

我已经成功提取了重复的大写:

library(stringr)
unlist(str_extract_all(gsub(">", "", tst), "(.)(?=\\1)"))
[1] "C" "C" "B" "B" "B" "C" "*" "*" "*" "*"

但我有点卡在这里。我的预感是返回索引的函数which 可能会有所帮助,但在这种情况下不知道如何实现它。

有什么想法吗?

编辑

我自己离解决方案并不远 - 只需使用 negative 前瞻(而不是积极的前瞻)就可以了:

str_extract_all(gsub(">", "", tst), "(.)(?!\\1)")
[[1]]
[1] "C" "B" "C" "*" "C"

[[2]]
[1] "A"

[[3]]
[1] "*" "B"

[[4]]
[1] "A" "*" "A" "*" "A"

[[5]]
[1] "*" "C"

[[6]]
[1] "A"

【问题讨论】:

    标签: r regex indexing


    【解决方案1】:

    我们可以使用gsub

    gsub("([A-Z*]>)\\1+", "\\1", tst)
    #[1] "C>B>C>*>C"
    

    为了得到第二个结果,去掉&gt;

    gsub(">", "", gsub("([A-Z*]\\>)\\1+", "\\1", tst) ,fixed = TRUE)
    #[1] "CBC*C"
    

    根据下面 OP 的 cmets,可能是

    gsub("(.)\\1+", "\\1", gsub(">", "", tst))
    #[1] "CBC*C"
    gsub("(.)\\1+", "\\1", gsub(">", "", "A>"))
    #[1] "A"
    gsub("(.)\\1+", "\\1", gsub(">", "", "A>A"))
    #[1] "A"
    gsub("(.)\\1+", "\\1", gsub(">", "", "A>A>A>A"))
    #[1] "A"
    

    【讨论】:

    • @ChrisRuehlemann 对此的期望是什么
    • A 仅此而已
    • @ChrisRuehlemann 这将返回 gsub("(.)\\1+", "\\1", gsub("&gt;", "", "A&gt;A&gt;A&gt;A"))# [1] "A"gsub("(.)\\1+", "\\1", gsub("&gt;", "", tst))# [1] "CBC*C" 上进行测试
    • @ChrisRuehlemann 与gsub("(.)\\1+", "\\1", gsub("&gt;", "", "A&gt;A"))# [1] "A" 相比,这也适用于gsub("((.)&gt;)\\1+\\2*", "\\2", "A&gt;A")# [1] "A&gt;A"
    【解决方案2】:

    获取CBC*C 的另一种方法是使用 2 个组并在替换中使用第 2 组。

    ((.)>)\1+
    

    Regex demo

    例子

    tst <- "C>C>C>B>B>B>B>C>C>*>*>*>*>*>C"
    gsub("((.)>)\\1+", "\\2", tst)
    

    输出

    [1] "CBC*C"
    

    【讨论】:

    • 这是一个很好的解决方案。
    • 为什么解决方案会在这样的情况下失败:gsub("((.)&gt;)\\1+", "\\2", "A&gt;A&gt;A&gt;A")?如何编辑它也适用于这种类型的案例?
    • @ChrisRuehlemann 这取决于预期的结果。当前模式重复捕获组以获取连续部分。替换使用内部的第二组。
    • @ChrisRuehlemann 它与最后一个 A 不匹配,因为它不在重复模式中。您可以选择匹配第二组 ((.)&gt;)\1+\2* 以匹配相同的尾随字符。 regex101.com/r/CldbL6/1
    • 非常感谢。事实证明,最好的解决方案确实是gsub("(.)\\1+", "\\1", gsub("&gt;", "", tst))。 @akrun 几分钟前发布了它。所以为了公平起见,我不得不接受他的回答。
    【解决方案3】:

    对于我们对正则表达式过敏的人:

    paste(rle(strsplit(tst, ">")[[1]])$values, collapse = ">") # or collapse = ""
    [1] "C>B>C>*>C"
    

    ...对于带有小写字母的字符串,当然会失败,例如"A&gt;A&gt;a&gt;a&gt;A&gt;A"

    【讨论】:

      【解决方案4】:

      一种没有正则表达式的通用 base R 方法。

      这里的想法是将字符串分解为组,然后依次删除重复模式(这使其与unique不同):

      tst <- "C>C>C>B>B>B>B>C>C>*>*>*>*>*>C"
      st <- paste(unlist(strsplit(tst,">")),collapse="")
      #[1] "CCCBBBBCC*****C"
      
      paste( unlist( sapply( 1:nchar(st), function(x){
        if( substr(st,x,x) != substr(st,(x+1),(x+1)) ){ substr(st,x,x) } } ) ), collapse="" )
      #[1] "CBC*C"
      

      哦,如果您需要小写功能(不包括小写字母),请改用:

      paste( unlist( sapply( 1:nchar(st), function(x){
        a=substr(st,x,x); b=substr(st,(x+1),(x+1));
        if( a != b & toupper(a) == a ){ a } else if( toupper(a) != a ){ a }  } ) ), collapse="" )
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-08-05
        • 2016-07-31
        • 1970-01-01
        • 1970-01-01
        • 2015-01-26
        • 2021-06-19
        • 2019-09-02
        • 1970-01-01
        相关资源
        最近更新 更多