2012-12-05 82 views
38

假設我們只給如何在JavaScript中給定其字符串名稱的對象屬性(的..的對象屬性)?

var obj = {}; 
var propName = "foo.bar.foobar"; 

我們怎樣才能物業obj.foo.bar.foobar設置爲特定值(顯示 「Hello World」)? 所以我想做到這一點,而我們只在一個字符串有屬性名稱:

obj.foo.bar.foobar = "hello world"; 
+3

重複的http://stackoverflow.com/questions/6842795/dynamic-deep-setting-for-a-javascript-object – Cerbrus

+1

這可能幫助別人誰是試圖瞭解這個問題的答案問題... http://stackoverflow.com/questions/39060905/how-recursion-takes-place-in-this-code-snippet –

回答

64
function assign(obj, prop, value) { 
    if (typeof prop === "string") 
     prop = prop.split("."); 

    if (prop.length > 1) { 
     var e = prop.shift(); 
     assign(obj[e] = 
       Object.prototype.toString.call(obj[e]) === "[object Object]" 
       ? obj[e] 
       : {}, 
       prop, 
       value); 
    } else 
     obj[prop[0]] = value; 
} 

var obj = {}, 
    propName = "foo.bar.foobar"; 

assign(obj, propName, "Value"); 
+1

是的,當路徑還不存在時, –

+1

@ Chiel92正是如此。 – VisioN

+0

對忍者的方式我的答案: - /好了,我猜。 – Cerbrus

12

因爲這個問題似乎是由不正確的答案來回答,我只是指從a similar question

正確的答案
function setDeepValue(obj, value, path) { 
    if (typeof path === "string") { 
     var path = path.split('.'); 
    } 

    if(path.length > 1){ 
     var p=path.shift(); 
     if(obj[p]==null || typeof obj[p]!== 'object'){ 
      obj[p] = {}; 
     } 
     setDeepValue(obj[p], value, path); 
    }else{ 
     obj[path[0]] = value; 
    } 
} 

用途:

var obj = {}; 
setDeepValue(obj, 'Hello World', 'foo.bar.foobar'); 
+2

嗯。你的答案看起來像我的一個確切的副本:) – VisioN

+0

它看起來像我在'typeof'中遺漏了'='。但坦率地說,'typeof'是唯一確保你不會分裂對象的方法,這是我碰到的一個問題。其餘的,這是簡單的遞歸。 – Cerbrus

+1

如果我用'foo.bar.foobar'和'foo.bar2.foobar2'調用它兩次會怎樣? – FrancescoMM

4

編輯:我ç重新編號爲jsPerf.com testcase以將接受的答案與我的版本進行比較。 原來我的版本更快,尤其是當你深入的時候。

http://jsfiddle.net/9YMm8/

var nestedObjectAssignmentFor = function(obj, propString, value) { 
    var propNames = propString.split('.'), 
     propLength = propNames.length-1, 
     tmpObj = obj; 

    for (var i = 0; i <= propLength ; i++) { 
     tmpObj = tmpObj[propNames[i]] = i !== propLength ? {} : value; 
    } 
    return obj; 
} 

var obj = nestedObjectAssignment({},"foo.bar.foobar","hello world"); 

+0

[這在Chrome瀏覽器中似乎稍慢](http://jsperf.com/object-for-vs-rec)比提議的遞歸函數。 **但是,IE/FF **的速度要快得多。 (仍然,Chrome的迭代次數最高,[V8](http://code.google.com/p/v8/)是一個執行JavaScript的野獸) – Cerbrus

+0

@Cerbrus顯示它取決於瀏覽器,創建了testcase我自己http://jsperf.com/nested-object-assignment –

+1

代碼性能測試101:不要爲每次迭代初始化您的函數 – Cerbrus

0

這裏是一個get和set功能,我剛剛從一對夫婦線程+一些自定義代碼的編譯。

它還會創建集合中不存在的密鑰。

function setValue(object, path, value) { 
    var a = path.split('.'); 
    var o = object; 
    for (var i = 0; i < a.length - 1; i++) { 
     var n = a[i]; 
     if (n in o) { 
      o = o[n]; 
     } else { 
      o[n] = {}; 
      o = o[n]; 
     } 
    } 
    o[a[a.length - 1]] = value; 
} 

function getValue(object, path) { 
    var o = object; 
    path = path.replace(/\[(\w+)\]/g, '.$1'); 
    path = path.replace(/^\./, ''); 
    var a = path.split('.'); 
    while (a.length) { 
     var n = a.shift(); 
     if (n in o) { 
      o = o[n]; 
     } else { 
      return; 
     } 
    } 
    return o; 
} 
3

所有的解決方案設置,所以我有一個具有以下扭捏的時候overid任何原始數據,使它成爲一個單一的對象太:

var obj = {} 
nestObject.set(obj, "a.b", "foo"); 
nestObject.get(obj, "a.b"); // returns foo  

var nestedObject = { 
    set: function(obj, propString, value) { 
     var propNames = propString.split('.'), 
      propLength = propNames.length-1, 
      tmpObj = obj; 
     for (var i = 0; i <= propLength ; i++) { 
      if (i === propLength){ 
       if(tmpObj[propNames[i]]){ 
        tmpObj[propNames[i]] = value; 
       }else{ 
        tmpObj[propNames[i]] = value; 
       } 
      }else{ 
       if(tmpObj[propNames[i]]){ 
        tmpObj = tmpObj[propNames[i]]; 
       }else{ 
        tmpObj = tmpObj[propNames[i]] = {}; 
       } 
      } 
     } 
     return obj; 
    }, 
    get: function(obj, propString){ 
     var propNames = propString.split('.'), 
      propLength = propNames.length-1, 
      tmpObj = obj; 
     for (var i = 0; i <= propLength ; i++) { 
      if(tmpObj[propNames[i]]){ 
       tmpObj = tmpObj[propNames[i]]; 
      }else{ 
       break; 
      } 
     } 
     return tmpObj; 
    } 
}; 

還可以更改的功能是一個Oject.prototype方法變更的obj參數去這樣的:

Object.prototype = { setNested = function(){ ... }, getNested = function(){ ... } } 

{}.setNested('a.c','foo') 
2

這裏有一個返回更新的對象

function deepUpdate(value, path, tree, branch = tree) { 
    const last = path.length === 1; 
    branch[path[0]] = last ? value : branch[path[0]]; 
    return last ? tree : deepUpdate(value, path.slice(1), tree, branch[path[0]]); 
} 

const path = 'cat.dog'; 
const updated = deepUpdate('a', path.split('.'), {cat: {dog: null}}) 
// => { cat: {dog: 'a'} } 
0

這是一個簡單的函數來做這個使用參考。

function setValueByPath (obj, path, value) { 
     var ref = obj; 

     path.split('.').forEach(function (key, index, arr) { 
      ref = ref[key] = index === arr.length - 1 ? value : {}; 
     }); 

     return obj; 
    } 
3

我知道這是一個古老的一個,但我看到的答案只有自定義函數。
如果您不介意使用庫,請看lodash_.set_.get函數。

0

您可以拆分路徑並檢查是否存在以下元素。如果不將對象分配給新屬性。

然後返回屬性的值。

最後賦值。

function setValue(object, path, value) { 
 
    var fullPath = path.split('.'), 
 
     way = fullPath.slice(), 
 
     last = way.pop(); 
 

 
    way.reduce(function (r, a) { 
 
     return r[a] = r[a] || {}; 
 
    }, object)[last] = value; 
 
} 
 

 
var object = {}, 
 
    propName = 'foo.bar.foobar', 
 
    value = 'hello world'; 
 

 
setValue(object, propName, value); 
 
console.log(object);

1

的一個非常直接的。

沒有遞歸或回調開銷。

function setDeepVal(obj, path, val) { 
 
    var props = path.split('.'); 
 
    for (var i = 0, n = props.length - 1; i < n; ++i) { 
 
    obj = obj[props[i]] = obj[props[i]] || {}; 
 
    } 
 
    obj[props[i]] = val; 
 
    return obj; 
 
} 
 

 

 

 
// TEST 
 
var obj = { hello : 'world' }; 
 
setDeepVal(obj, 'foo.bar.baz', 1); 
 
setDeepVal(obj, 'foo.bar2.baz2', 2); 
 
console.log(obj);

相關問題