String replacement question

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)

• 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, 2013 at 8:05

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]]

• Yes I thought it was a really elegant solution - thanks so much! Nov 25, 2013 at 9:47
• Elegant and faster! +1 Nov 25, 2013 at 19:12

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

StringReplaceList[string, Thread["<ALK>" -> alk]]
(* {"methene", "ethene", "propene", "butene", "pentene"} *)

• Ha that's really elegant too - don't know now which answer to mark as the right one - they're all perfect :-) Nov 22, 2013 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? Nov 23, 2013 at 22:32
• @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, 2013 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... Nov 23, 2013 at 22:50

Is this what you want?

StringReplace[string, "<ALK>" -> #] & /@ alk

• Ha that's perfect - thanks so much - other answer is great too btw! :-) Nov 22, 2013 at 1:31

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.

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";

(* {"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

• 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? Nov 23, 2013 at 22:33
• Ha many thanks - that's great!! Nov 24, 2013 at 9:33
• 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 Nov 24, 2013 at 9:46