Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

If you have a string

string="<ALK>ene"

and I have a list alk={"meth", "eth", "prop", "but", "pent"}, what function could I use to automatically create a list {"methene","ethene","propene","butene","pentene"}, i.e. with "<ALK>" replaced by each element of alk in turn? I could do something like alkenenames[ALK_] := StringJoin[ALK, "ene"];Map[alkenenames, alknames]; - but instead of defining functions I would like to be able to define string with pattern replacement rules embedded in them as above. How do I do this in Mathematica?

Edit: actually one more question - I noticed that the different proposed solutions below behave differently if the string happens not to contain "<ALK>" - in one solution returning an empty list, and in another one returning a list of your original string duplicated n times, where n is the length of the list alk. In actual fact I would like the function to just return the original string if <ALK> does not occur in it. Also, I was wondering what would be the most elegant solution to also allow multiple occurences of <ALK> in the string, which would then be replaced in a combinatorial fashion. (i.e. replacing the first occurrence of <ALK> by each alk element in turn, and then replacing the second occurrence of <ALK> by each alk element in turn as well, and so on)

share|improve this question
1  
This is one solution to both issues in your updated question: Block[{string = "<ALK>yl <ALK>one <ALK>elone", pos}, pos = StringPosition[string, "<ALK>"]; StringReplacePart[string, {##}, pos] & @@@ Tuples[alk, Length@pos]] (If string doesn't contain "<ALK>" at all, then it just returns it without change.) –  Aky Nov 24 '13 at 8:05
add comment

5 Answers 5

up vote 4 down vote accepted

I guess it wouldn't hurt to post my comment as an answer, since it's sufficiently different from the others posted so far:

combinatorialStringReplace[haystack_String, needle_String, 
  replacements_List] := 
 Module[{positions = StringPosition[haystack, needle]}, 
  StringReplacePart[haystack, #, positions] & /@ 
   Tuples[replacements, Length@positions]]
share|improve this answer
    
Yes I thought it was a really elegant solution - thanks so much! –  Tom Wenseleers Nov 25 '13 at 9:47
    
Elegant and faster! +1 –  Aisamu Nov 25 '13 at 19:12
add comment

I finally get to use StringReplaceList for the first time ever!

StringReplaceList[string, Thread["<ALK>" -> alk]]
(* {"methene", "ethene", "propene", "butene", "pentene"} *)
share|improve this answer
    
Ha that's really elegant too - don't know now which answer to mark as the right one - they're all perfect :-) –  Tom Wenseleers Nov 22 '13 at 8:35
    
In my edited question I point out a couple of remaining issues - would you know by any chance how to deal with those as well? –  Tom Wenseleers Nov 23 '13 at 22:32
1  
@TomWenseleers I don't understand your question... If a 'string' doesn't have "", then it is not a string. Your addendum asks about combinatorial replacements... that's rather different from the original focus of this question and I would recommend asking a new question instead. –  rm -rf Nov 23 '13 at 22:35
    
Ha sorry something went wrong with the formatting in my edit, I fixed it now - hope it's clear now... I left my additional question about dealing with multiple occurrences, since it's a case that I think could also occur routinely, and it would be nice to have a generic solution... –  Tom Wenseleers Nov 23 '13 at 22:50
add comment

Is this what you want?

StringReplace[string, "<ALK>" -> #] & /@ alk
share|improve this answer
    
Ha that's perfect - thanks so much - other answer is great too btw! :-) –  Tom Wenseleers Nov 22 '13 at 1:31
add comment

This is really overkill here, but since we were talking about string templates in the chatroom today, here's another way:

alk = {"meth", "eth", "prop", "but", "pent"}

string = "``ene"

ToString@StringForm[string, #] & /@ alk

The ToString@ part was necessary to create real strings, as opposed to StringForm expressions that display in a certain way but still have StringForm as head.

share|improve this answer
add comment

There is also the hand-rolled, naive solution:

First@StringCases[string, pre___ ~~ "<ALK>" ~~ post___ :> pre <> # <> post] & /@ alk

Please note that the answers given so far behave differently for a string with multiple "slots".

alk = {"a", "b"};
string = "<P>00<P><P>0";

StringReplaceList[string, Thread["<P>" -> alk]]
(* {"a00<P><P>0", "b00<P><P>0", "<P>00a<P>0",
    "<P>00b<P>0", "<P>00<P>a0", "<P>00<P>b0"} *)

StringReplace[string, "<P>" -> #]& /@ alk
(* {"AAA00AAAAAA0", "BBB00BBBBBB0"} *)

Comparing the speed of the proposed solutions, with ~500k single-slot strings

alk = {"meth", "eth", "prop", "but", "pent"};
strings = Tuples[{CharacterRange["a", "z"], CharacterRange["a", "z"],
                  {"<ALK>"},
                  CharacterRange["a", "z"], CharacterRange["a", "z"]}]

(* rm -rf *)
StringReplaceList[strings, Thread["<ALK>" -> alk]] // Timing // First
(* 2.555044 *)

(* Jason B. *)
StringReplace[strings, "<ALK>" -> #] & /@ alk // Timing // First
(* 0.901540 *)

(* Hand-rolled, naive StringReplace *)
StringCases[strings, pre___ ~~ "<ALK>" ~~ post___ :> pre <> # <> post] & /@ alk // Timing // First
(* 3.760633 *)

(* Szabolcs *)
stringsz = strings;
stringsz[[All,3,All]] = "``";
Thread[StringForm[#, alk]] & /@ stringsz // Timing // First
(* 1.157345 *)

(* All of the solutions are a Flatten and Sort away from equality *)

Response to OP's edit

In actual fact I would like the function to just return the original string if does not occur in it. [...] Also, I was wondering what would be the most elegant solution to also allow multiple occurences of in the string, which would then be replaced in a combinatorial fashion.

Not exactly elegant, but does the trick

yourStringReplaceList[s_List, patt_] :=
    yourStringReplaceList[#, patt] & /@ Flatten[s];

yourStringReplaceList[s_String, patt_] := Module[{rep},
    rep = StringReplaceList[s, patt];
    If[Length@rep == 0, {s}, rep]
]

FixedPoint[yourStringReplaceList[#, Thread["<ALK>" -> alk]] &, string] // Flatten // Union
share|improve this answer
    
In my edited question I point out a couple of remaining issues - would you know by any chance how to deal with those as well? –  Tom Wenseleers Nov 23 '13 at 22:33
    
Ha many thanks - that's great!! –  Tom Wenseleers Nov 24 '13 at 9:33
1  
The solution posted as a comment Block[{string = "<ALK>yl<ALK>ane", pos}, pos = StringPosition[string, "<ALK>"]; StringReplacePart[string, {##}, pos] & @@@ Tuples[alk, Length@pos]] also works btw –  Tom Wenseleers Nov 24 '13 at 9:46
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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