The answer from @chris is excellent, I just want to propose an alternative solution using regular expressions that work the other way around, i.e. they don’t look for the appearance of “placeholder versions” of all elements in the map properties, but repeatedly looking at the occurrences of the placeholder itself and substituting it with the corresponding value from the property map. This has two advantages:
- If the property map grows very large, this solution should have better performance (still for comparison).
- Placeholder and substitution work can be easily changed by adjusting the regular expression and the substitution function (this may not be a problem).
The disadvantage is, of course, that the code is a bit more complicated (partly due to the fact that JavaScript does not have a nice way of substituting regular expression matches using custom functions, so what is substituteRegExp ):
function substituteRegExp(string, regexp, f) { // substitute all matches of regexp in string with the value // returned by f given a match and the corresponding group values var found; var lastIndex = 0; var result = ""; while (found = regexp.exec(string)) { var subst = f.apply(this, found); result += string.slice(lastIndex, found.index) + subst; lastIndex = found.index + found[0].length; } result += string.slice(lastIndex); return result; } function templateReplace(string, values) { // repeatedly substitute [key] placeholders in string by values[key] var placeholder = /\[([a-zA-Z0-9]+)\]/g; while (true) { var newString = substituteRegExp(string, placeholder, function(match, key) { return values[key]; }); if (newString == string) break; string = newString; } return string; } alert(templateReplace("hello [[b]] [my] [name]", { "name":"world", "my":"beautiful", "a":"[b]", "b":"c", "c":"my" })); // -> "hello my beautiful world"
Update . I slightly adjusted the comparison of the two solutions (jsFiddle at http://jsfiddle.net/n8Fyv/1/ , I also used Firebug). Although the @chris solution is faster for small lines (no regular expression analysis, etc.), this solution is much better for large lines (of the order of thousands of characters). I did not compare for different sizes of the property map, but I expected even big differences there.
Theoretically, this solution has runtime O (kn), where k is the nesting depth of the placeholders and n is the string length (provided that the dictionary / hash search requires constant time), while the @chris solution is O (knm), where m - the number of elements on the property map. Of course, all this applies only to large entrances.