2010-01-14 77 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; 
} 

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