模板和占位符算法

时间:2012-01-23 22:09:30

标签: javascript algorithm templates placeholder

首先快速定义:)

  • 模板 - 可能包含占位符的字符串(例如:“hello [name]”)
  • 占位符 - 子字符串whitin方括号(例如:“hello [name]中的”name“)。
  • 属性映射 - 字符串值为
  • 的有效对象

我需要编写一个代码,用属性映射中的匹配值替换占位符(以及括号)。

例如: 对于以下属性映射:

{
    "name":"world",
    "my":"beautiful",
    "a":"[b]",
    "b":"c",
    "c":"my"
}

预期结果:

  • “你好名字” - > “你好名字”

  • “你好[姓名]” - > “你好世界”

  • “[b]” - > “C”

  • “[a]” - > “c”(因为[a] - > [b] - > [c])

  • “[[b]]” - > “我的”(因为[[b]] - > [c] - > my)

  • “你好[我的] [姓名]” - > “你好美丽的世界”

3 个答案:

答案 0 :(得分:2)

var map = {
    "name":"world",
    "my":"beautiful",
    "a":"[b]",
    "b":"c",
    "c":"my"
};

var str = "hello [my] [name] [[b]]";

do {
    var strBeforeReplace = str;
    for (var k in map) {
        if (!map.hasOwnProperty(k)) continue;
        var needle = "[" + k + "]";
        str = str.replace(needle, map[k]);
    }
    var strChanged = str !== strBeforeReplace;
} while (strChanged);

document.write(str); //hello beautiful world my

答案 1 :(得分:2)

@chris的答案非常好,我只是想提供一种替代解决方案,使用正则表达式“反过来”,即不是通过查找属性中所有项目的“占位符版本”的出现map,但通过反复查找占位符本身的出现次数,并用属性映射中的相应值替换它。这有两个好处:

  1. 如果属性映射变得非常大,则此解决方案应该具有 更好的表现(尽管仍然需要进行基准测试)。
  2. 通过调整正则表达式和替换函数,可以轻松修改占位符和替换工作方式(这可能不是问题)。
  3. 当然,缺点是代码有点复杂(部分原因是JavaScript缺少使用自定义函数替换正则表达式匹配的好方法,因此这是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"
    

    更新:我做了一些小的分析来比较两个解决方案(jsFiddle在http://jsfiddle.net/n8Fyv/1/,我也使用了Firebug)。虽然@chris的解决方案对于小字符串来说更快(不需要解析正则表达式等),但是对于大字符串(大约数千个字符),此解决方案执行得更好。我没有比较不同尺寸的属性地图,但期望更大的差异。

    理论上,此解决方案具有运行时O( k n ),其中 k 是占位符和 n的嵌套深度是字符串的长度(假设字典/散列查找需要恒定时间),而@chris'解决方案是O( k n m < / em>)其中 m 是属性映射中的项目数。当然,所有这些只与大量投入有关。

答案 2 :(得分:0)

如果您熟悉.NET的String.Format,那么您应该看一下this JavaScript implementation。它也支持数字格式,就像String.Format一样。

以下是如何使用它的示例:

var result = String.Format("Hello {my} {name}", map);

但是,需要进行一些修改才能执行递归模板。