7

This seems simple:

Given

lis = {"abcd12efcdef"}

I would like to delete all instances of "ef" when it directly follows a digit character, to give:

res = {"abcd12cdef"}
E. Chan-López
  • 23,117
  • 3
  • 21
  • 44
Suite401
  • 4,793
  • 8
  • 18

3 Answers3

9

One possible way could be:

lis = {"abcd12efcdef"};
StringReplace[#, k : DigitCharacter ~~ "ef" :> k, 1] & //@ lis

{"abcd12cdef"}

EDIT

lis = {"abcd12efcdef", "abcd12efcdefa12efghef21"};
res1 = StringReplace[#, k : DigitCharacter ~~ "ef" :> k, 1] & //@ lis
res2 = StringReplace[#, k : DigitCharacter .. ~~ "ef" :> k] & /@ lis

{"abcd12cdef", "abcd12cdefa12ghef21"}

res1 == res2

True

res2 is more idiomatic than res1.

Syed
  • 52,495
  • 4
  • 30
  • 85
  • 1
    +1 but I think :> intead of -> would be better. – Nasser Aug 30 '22 at 04:58
  • Thanks, I will edit this in. – Syed Aug 30 '22 at 05:00
  • 1
    @Nasser there's no need to delay the replacement in this case. In your opinion, what makes :> "better" than -> when the right-hand side does not need delaying? – Roman Aug 30 '22 at 07:55
  • 2
    @Roman it is just a rule of thumb I use. Whenever the symbol shows up on the left hand (k here), better use :> when it also shows on the rhs. I know in this case it is the pattern name itself. But so not to worry, I just use :> for all cases. Here is a link post with screen shot from the book which recommends this. so delayed :> is safer in general. – Nasser Aug 30 '22 at 08:30
8

Using a regular expression with positive look-behind:

s = "abcd12efcdef";
StringDelete[s, RegularExpression["(?<=\\d)ef"]]
(*    "abcd12cdef"    *)

Explanations:

  • \\d is a digit character
  • (?<=...) is a positive look-behind: in this case we only match ef if it is preceded by \\d (but we don't include the \\d in the match)
Roman
  • 47,322
  • 2
  • 55
  • 121
2

With rules, it is easier, as @Roman and @Syed answers show. However, here is a way to do it without rules:

    MyStringReplace[Pattern[lis, 
Blank[List]], Pattern[string2, 
Blank[]] ? StringQ] := {
    StringJoin[
            Delete[Flatten @ Map[Characters, lis],
                Outer[List,
                    Flatten[
                        Map[
                                Function[
                                    If[
      SameQ[DigitQ[Extract[Flatten @ Map[Characters, lis], {#}]], 
       True],
                                    Range[# + 1, # + 2],
                                    Nothing
                                ]
                            ],
                            Map[
                                (# - 1 &amp;) @* ((Apply[Times, #] &amp;) @* First),
                                Transpose[
                                    Map[

       Function @ Position[Flatten @ Map[Characters, lis], #],
                                        Characters @ string2
                                    ]
                                ]
                            ]
                        ]
                ]
            ]
        ]
    ]

};

Examples:

lis0 = {"abcd12efcdef"};
lis1 = {"abcd12efcdefab3ef"};
lis2 = {"abcd12efcdefa12efghef21"};

Tests:

MyStringReplace[lis0, "ef"]
(*{"abcd12cdef"}*)
MyStringReplace[lis1, "ef"]
(*{"abcd12cdefab3"}*)
MyStringReplace[lis2, "ef"]
(*{"abcd12cdefa12ghef21"}*)
E. Chan-López
  • 23,117
  • 3
  • 21
  • 44
  • 1
    A related question, so I won't post it as a new question. I have lis = {"1","a","b","23","c","d","e" and need to join the adjacent non-digit characters to give: res={"1","ab","23","cde". – Suite401 Aug 30 '22 at 16:48