8
$\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$

5 Answers 5

10
$\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$
10
$\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
    Commented Aug 30, 2022 at 4:58
  • $\begingroup$ Thanks, I will edit this in. $\endgroup$
    – Syed
    Commented Aug 30, 2022 at 5:00
  • 2
    $\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
    Commented 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
    Commented Aug 30, 2022 at 8:30
4
$\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
    Commented Aug 30, 2022 at 16:48
4
$\begingroup$
s = "abcd12efcdefa12efghef21";

Using SequencePosition and Threaded (new in 13.1)

With[{c = Characters @ s},
 StringJoin @ Fold[Drop, c, SequencePosition[c, {_?DigitQ, "e", "f"}] + 
  Threaded[{1, 0}] // Reverse]]

"abcd12cdefa12ghef21"

$\endgroup$
3
$\begingroup$

Also

StringReplace[#, RegularExpression["(\\d)(ef)"] -> "$1"] & /@ lis

(* {"abcd12cdef"} *)

(Keep just the first match)

$\endgroup$
1
  • 1
    $\begingroup$ +1 - I should learn RegularExpression $\endgroup$
    – eldo
    Commented Jun 12 at 9:44

Your Answer

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

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