将点表示法转换为方括号表示法的Javascript正则表达式

8
考虑以下 JavaScript 代码:
var values = {
    name: "Joe Smith",
    location: {
        city: "Los Angeles",
        state: "California"
    }
}

var string = "{name} is currently in {location.city}, {location.state}";

var out = string.replace(/{([\w\.]+)}/g, function(wholematch,firstmatch) {
    return typeof values[firstmatch] !== 'undefined' ? 
        values[firstmatch] : wholematch;
});

这将输出以下内容:
Joe Smith is currently in {location.city}, {location.state}

但我想输出以下内容:
Joe Smith is currently in Los Angeles, California

我正在寻找一种好的方法,将字符串中花括号之间发现的多级点符号转换为多个参数,以便与方括号符号一起使用,就像这样:
values[first][second][third][etc]

本例中,我需要找出哪个正则表达式字符串和函数可以得到与以下等效的结果:

out = values[name] + " is currently in " + values["location"]["city"] +
    values["location"]["state"];

注意: 我希望在不使用eval()的情况下完成此操作。


刚刚注意到一个打字错误。将 var joe 重命名为 var values - Tauren
另一个类似的问题有一个替代方案,由@J-P提供,看起来相当优雅:https://dev59.com/ZE_Sa4cB1Zd3GeqP_1yD#3344487 - Tauren
3个回答

12

使用辅助函数来迭代访问属性:

function getNestedValue(obj, prop) {
  var value, props = prop.split('.'); // split property names

  for (var i = 0; i < props.length; i++) {
    if (typeof obj != "undefined") {
      obj = obj[props[i]]; // go next level
    }
  }
  return obj;
}

var string = "{name} is currently in {location.city}, {location.state}";

var out = string.replace(/{([^}]+)}/g, function(wholematch,firstmatch) {
  var value = getNestedValue(joe, firstmatch);
  return typeof value !== 'undefined' ? value : wholematch;
});
// "Joe Smith is currently in Los Angeles, California"

请在这里尝试上述示例。

编辑: 下面是稍微更加简洁优雅的解决方案,使用新的ECMAScript 5th Edition标准的一部分:Array.prototype.reduce方法:

function replace(str, obj) {
  return str.replace(/{([^}]+)}/g, function(wholematch,firstmatch) {
    var value = firstmatch.split('.').reduce(function (a, b) {
      return a[b];
    }, obj);
    return typeof value !== 'undefined' ? value : wholematch;
  });
}

replace("{name} is currently in {location.city}, {location.state}", values);
// "Joe Smith is currently in Los Angeles, California"

请尝试新的示例这里


@ CMS:不错,这可行!但它似乎不像基于正则表达式的解决方案那样优雅,我想相信有一种正则表达式的方法来解决它。然而,也许追求正则表达式的解决方案会浪费时间。 - Tauren
@CMS:你的正则表达式是否允许大括号之间有任何内容?如果我想将值限制为有效的JavaScript变量名[a-zA-Z0-9_\.],最好使用/{([\w\.]+)}/g吗?实际上,这会允许在开头有一个句点,这是不允许的,但很接近。哦对了,我认为我还需要包括$ - Tauren
@Tauren,没错,它有点宽容,我并不太在意,因为当你使用括号表示法访问属性(或在对象字面量中使用字符串)时,没有限制,例如 obj["3.xx\u0009"] ... - Christian C. Salvadó
@CMS:非常酷!可惜reduce还没有被普遍支持,但我猜Array.prototype.reduce并不是那么重要需要包含。 - Tauren
@Tauren,这不是什么大问题,标准兼容版本可以在这里找到(https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Global_Objects/Array/Reduce#Compatibility)...IE是唯一缺乏此标准方法的主流浏览器... - Christian C. Salvadó

0

我不确定如何在Javascript中集成这个,但是这里有一个使用正则表达式进行所需转换的Java片段:

    String[] tests = {
        "{name}",
        "{location.city}",
        "{location.state.zip.bleh}",
    };
    for (String test : tests) {
        System.out.println(test.replaceAll("\\{?(\\w+).?", "[$1]"));
    }

这将打印:

[name]
[location][city]
[location][state][zip][bleh]

本质上,它将所有出现的\{?(\w+).?替换为[$1]


嗯,有趣。然而,即使我在JS中让它工作,我认为最终会得到一个需要“eval”的字符串,这是我想避免的。 - Tauren

0
你可以查看在comp.lang.javascript发布的主题无错误的深度属性访问? - 这提供了几种实现你想要的功能的方法。
对于正则表达式,你只需要使用/{.*?}/g

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接