I have a list of strings of the form

list = {str1, str2, strx, str3, .., str10}

each string contains both letters and numbers, except for strx which does not contain numbers. What I want to do is attach strx to the element that immediately precedes it, producing

{str1, {str2, strx}, ..., str10}

I am using

 list /. 
   {bef___, PatternSequence[x_, m_] /; StringFreeQ[m, DigitCharacter], aft___} :> 
     {bef, {x, m}, aft}

However this seems to match only the first occurrence of the pattern I'm looking for. How can I make it match several cases? I also want to be able to process a list that does not contain any of the strx.

  • 2
    $\begingroup$ Can there be strxs in sequence, e.g. {...,strn,strx,strx,strn...}? Can there be a strx at the start of the list, and if so, what is to be done? You need to specify your problem much more precisely. $\endgroup$
    – ciao
    Jul 27, 2015 at 5:25
  • $\begingroup$ Good catch, no, the strx do not follow each other. $\endgroup$
    – amrods
    Jul 27, 2015 at 7:47
  • $\begingroup$ Well, actually i found out that there are some cases in which up to 3 strx follow a single stri. In that case, I would like to attach each of those strx to the stri that immediately precedes it: {str1, strx1, srtx2, strx3, str2} should result in {{str1, strx1, strx2, strx3}, str2} $\endgroup$
    – amrods
    Jul 27, 2015 at 20:25
  • $\begingroup$ Oh, and no, an strx will never be at the beginning. $\endgroup$
    – amrods
    Jul 27, 2015 at 20:27
  • 3
    $\begingroup$ It is seriously uncool to fundamentally change the objective, particularly after already answering a question about the change oppositely. $\endgroup$
    – ciao
    Jul 27, 2015 at 23:24

3 Answers 3


Here is one way:


 lines //. {bef___, 
     m_String?(StringFreeQ[#, DigitCharacter] &)], 
    aft___} :> {bef, {Hold[x], m}, aft}]

(* ==> {"123", {"312", "anb"}, {"452", "Xys"}} *)

I changed the pattern so that it tests the Head of x and m as well, and wrap x in Hold in the replacement. That way, when I then do ReplaceRepeated, the Hold will prevent an infinite recursion.

Edit: simplicity and speed

If you don't insist on using PatternSequence, I would strongly recommend doing it this way:

lines = {"123", "127", "312", "anb", "45h2", "Xys"}

Split[lines, StringFreeQ[#2, DigitCharacter] &] /. {x_} :> x

(* ==> {"123", "127", {"312", "anb"}, {"45h2", "Xys"}} *)

This uses the second argument of Split to test the subsequent element for every list member and checks that it satisfies the condition. Then it groups the two elements surrounding the position where this result changes into a list.

  • $\begingroup$ Interesting idea, but performance is.... interesting. Lists with no elements with no digits are done reasonably fast. Adding just one element with that condition clobbers performance, adding many....things get ugly... $\endgroup$
    – ciao
    Jul 27, 2015 at 6:23
  • 1
    $\begingroup$ @ciao Well, the question was about PatternSequence... $\endgroup$
    – Jens
    Jul 27, 2015 at 17:43
  • $\begingroup$ @ciao I added an alternative solution that is also "interesting". $\endgroup$
    – Jens
    Jul 27, 2015 at 18:30
  • $\begingroup$ Second is pretty sweet, appears to be second fastest so far. +1 on that. $\endgroup$
    – ciao
    Jul 27, 2015 at 23:19
  • $\begingroup$ @ciao Are you sure you want to delete your A? It can still teach readers a lot. Anyway, luckily my second approach still seems to work for the modified Q, too. $\endgroup$
    – Jens
    Jul 28, 2015 at 0:02

An approach using ReplaceRepeated and a temporary tag.

data = 
  {"DD1", "B12", "CCC", "3AD", "C2A", "3D1", "1A1", "C11", "BBA", 
   "322", "1D2", "B3C", "1BD", "CC1", "AC"};

list //. 
   PatternSequence[x_, m_] /; Head[m] =!= tag && StringFreeQ[m, DigitCharacter], 
   aft___} :> {bef, tag[x, m], aft} /. 
     tag -> List
{"DD1", {"B12", "CCC"}, "3AD", "C2A", "3D1", "1A1", {"C11", "BBA"}, 
 "322", "1D2", "B3C", "1BD", {"CC1", "AC"}}
  • $\begingroup$ You must have posted this after I left - it's similar to what I did, but nonetheless an independent solution (+1). $\endgroup$
    – Jens
    Jul 27, 2015 at 18:04

One option is to apply the rule over and over again until it doesn't have any effect:

list = {"DD1", "B12", "CCC", "3AD", "C2A", "3D1", "1A1", "C11", "BBA",
    "322", "1D2", "B3C", "1BD", "CC1", "AC"};

FixedPoint[Replace[#, {
     PatternSequence[x_, m_] /; StringFreeQ[m, DigitCharacter],
     } :> {bef, {x, m}, aft}] &,

(* Out: {"DD1", {"B12", "CCC"}, "3AD", "C2A", "3D1", "1A1", {"C11", 
  "BBA"}, "322", "1D2", "B3C", "1BD", {"CC1", "AC"}} *)

You may also consider this approach to solving your problem:

replacePair[{str1_, str2_}] := Module[{freeQ1, freeQ2},
  freeQ1 = StringFreeQ[str1, DigitCharacter];
  freeQ2 = StringFreeQ[str2, DigitCharacter];
  If[freeQ1, ## &[], If[freeQ2, {str1, str2}, str1]]
replacePair[{str_}] := If[StringFreeQ[str, DigitCharacter], ## &[], str]

Developer`PartitionMap[replacePair, list, 2, 1, {1, 1}, {}]

(* Out: {"DD1", {"B12", "CCC"}, "3AD", "C2A", "3D1", "1A1", {"C11", 
  "BBA"}, "322", "1D2", "B3C", "1BD", {"CC1", "AC"}} *)
  • $\begingroup$ Wouldn't //. be appropriate here? $\endgroup$ Jul 27, 2015 at 5:52
  • $\begingroup$ @Guesswhoitis. Jens showed how to do that, the reason I used Replace instead is because it only works on the top level so I don't have to do the Hold/ReleaseHold trick. $\endgroup$
    – C. E.
    Jul 27, 2015 at 5:57

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.