7
$\begingroup$

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"}
$\endgroup$

3 Answers 3

8
$\begingroup$

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)
$\endgroup$
9
$\begingroup$

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.

$\endgroup$
4
  • 1
    $\begingroup$ +1 but I think :> intead of -> would be better. $\endgroup$
    – Nasser
    Aug 30, 2022 at 4:58
  • $\begingroup$ Thanks, I will edit this in. $\endgroup$
    – Syed
    Aug 30, 2022 at 5:00
  • 1
    $\begingroup$ @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? $\endgroup$
    – Roman
    Aug 30, 2022 at 7:55
  • 2
    $\begingroup$ @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. $\endgroup$
    – Nasser
    Aug 30, 2022 at 8:30
2
$\begingroup$

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 &) @* ((Apply[Times, #] &) @* 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"}*)
$\endgroup$
1
  • 1
    $\begingroup$ 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". $\endgroup$
    – Suite401
    Aug 30, 2022 at 16:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.