2010-01-14 44 views
2

我正在研究一个小库,它可以让我使用对象进行一些基本的键值编码。说我有以下对象:Javascript对象键值编码。动态设置嵌套值

var data = { key1: "value1", key2: { nested1: 1, nested2: "wowza!" } }; 

而且我有以下的JavaScript函数:

var setData = function(path, value) { 
    eval("data." + path + "= value;"); 
}; 

而且在使用中:

setData("key1", "updated value1"); // data.key1 == "updated value1" 
setData("key2.nested1", 99); // data.key2.nested1 == 99 

这个工作,但是我想完成以上不使用eval。这是可能的,还是eval最好的办法?

编辑:

注意可以假设你设置的值存在,至少在路径深度 - 1,我更关心的是设置一个现有对象的值。

回答

16

递归是你所需要的:

function setData(key,val,obj) { 
    if (!obj) obj = data; //outside (non-recursive) call, use "data" as our base object 
    var ka = key.split(/\./); //split the key by the dots 
    if (ka.length < 2) { 
    obj[ka[0]] = val; //only one part (no dots) in key, just set value 
    } else { 
    if (!obj[ka[0]]) obj[ka[0]] = {}; //create our "new" base obj if it doesn't exist 
    obj = obj[ka.shift()]; //remove the new "base" obj from string array, and hold actual object for recursive call 
    setData(ka.join("."),val,obj); //join the remaining parts back up with dots, and recursively set data on our new "base" obj 
    }  
} 

setData("key1", "updated value1"); // data.key1 == "updated value1" 
setData("key2.nested1", 99); // data.key2.nested1 == 99 
+1

我希望我可以高举这100次 - 这正是我期待的!谢谢! – 2011-11-18 16:42:30

+0

**非常感谢**!我无法开始告诉你这对我有多大帮助。 – 2014-08-19 19:50:25

+0

这是一个很好的答案。我创建了一个没有'obj'参数的版本,并且使用ES6语法来实现易读性。 https://gist.github.com/sungwoncho/8caf7f3d9e56741c0631cda134e01832 – 2016-07-01 05:35:44

4

是的,这很容易。 obj.prop = val是一样的obj['prop'] = val所以你可以重写使用setData就象这样:

var setData = function (path, value) { 
    data[path] = value; 
}; 

而且应该这样做。但是,我不确定你会在第三级获得多少成功(如果这样做有道理的话。)obj.prop.prop不能被obj ['prop.prop']引用,而是会有被obj ['prop'] ['prop']引用,所以setData将不得不被重写来考虑这一点。尽管如此,我没有太多运气。

编辑:我做了一个(对我来说)设置它嵌套如何你想要的,但没有比这更进一步。不幸的是,如果你的嵌套比例子更深,那么我不认为放弃eval的真正理由。我没有做任何基准测试,但在某些时候,计算结果将比计算更昂贵,甚至比eval(这很难完成。)

这就是我想出的,它将它们设置为至少1'水平'深;也就是说,它将与key2.nested1但不key2.nested1.i_love_nesting工作,如果是有道理的:

var setData = function (path, value) { 
    if (path.indexOf('.') != -1) { 
     path = path.split('.'); 
     for (var i = 0, l = path.length; i < l; i++) { 
      if (typeof(data[path[i]]) === 'object') { 
       continue; 
      } else { 
       data[path[i - 1]][path[i]] = value; 
      } 
     } 
    } else { 
     data[path] = value; 
    } 
}; 

希望这有助于。以最有效的方式,我可能不会写这个,虽然...

+0

不幸的是,这不适用于嵌套值。在你的例子中: setData(“prop.nestedProp”,“val”); 将导致以下数据JSON: { “prop.nestedProp”: “VAL”} 我想: {道具:{nestedProp: “VAL”}} – 2010-01-14 00:22:42

+0

是啊,我抓住了一个几秒钟后,我提交了帖子。花了我一点时间来获得一个很好的工作函数,但上面的那个函数应该这样做。您的图书馆的另一种选择可能是让他们直接访问“对象” - 即让他们访问“my_json_object.foo.bar”。 – Reid 2010-01-14 00:43:36

+0

完整的函数(为了简单起见除外)将在数值发生变化时触发事件。 app.dataSet = function(path, value) { if (app.dataGet(path) == value) return value; eval("app._data." + path + "= value;"); app.trigger("data." + path); return app.dataGet(path); }; 不幸的是,这需要在任意深度工作。我希望可能有一个很好的解决方案,让get函数(必须测试它是否比使用eval更快)。 感谢您的帮助。 – 2010-01-14 01:09:16

0
function setData(key,val,obj){ 
    keys = key.split(/\./); 
    to_set = keys.pop(); 
    keys.forEach(function(obj_name){ obj = obj[obj_name]; }); 
    obj[to_set] = val; 
} 
+0

你能否提供你的代码的描述? – cereallarceny 2012-10-21 05:06:38

0

我觉得这个问题变得更简单,更自然的解决,如果你能在指定路径方面灵活。如果不是做这样的事情key2.nested1你可以做{key2:{nested1:{}}}(对象符号),它变得相当简单:

function setData(subtree, path){ 
    var nodeName = Object.keys(path); 

    if(typeof path[nodeName] == 'object') 
     setData(subtree[nodeName], path[nodeName]); 
    else 
     subtree[nodeName] = path[nodeName]; 
} 

var data = { key1: "value1", key2: { nested1: 1, nested2: "wowza!" } }; 

// For the initial call, 'subtree' is the full data tree 
setData(data, {key2:{nested1:99}}); 

你只需递归到setData呼叫的第二ARG,直到找到值。每次递归时,您都会在指定的点处进入data树。

该函数可以很容易地修改为接受你指定的点符号,但对我来说,这是一种更清洁和更自然的方法(加上,字符串操作很慢)。

干杯

0

从@启发user1416920我做了这个一个:

function setData(key,val,obj) 
{ 
    keys = key.split(/\./); 
    last = keys.pop(); 

    keys.forEach(function(key) 
    { 
     if(typeof obj[key] === "undefined") 
     obj[key] = {}; 
     obj = obj[key]; 
    }); 

    obj[last] = val; 
} 

它的作用是非常自我解释^^。