2015-01-21 88 views
5

我有一堆像點分隔字符串進來的對象屬性,例如"availability_meta.supplier.price",我需要爲record['availability_meta']['supplier']['price']等分配相應的值。Javascript:將點分隔字符串轉換爲嵌套對象值

並非所有事物都是3級深度:許多隻有1級深度,許多深度超過3級。

有沒有一種很好的方式來在Javascript中以編程方式賦值?例如,我需要:

["foo.bar.baz", 1] // --> record.foo.bar.baz = 1 
["qux.qaz", "abc"] // --> record.qux.qaz = "abc" 
["foshizzle", 200] // --> record.foshizzle = 200 

我想我可以砍的東西在一起,但我沒有記住什麼好的算法,以便將不勝感激建議。如果這有幫助,我會使用lodash,並向其他圖書館開放,以便迅速處理此問題。

編輯這是在後端,並不經常運行,所以沒有必要優化大小,速度等。事實上,代碼的可讀性將成爲未來開發者的一個優點。

編輯2這是不相同的引用重複。也就是說,我需要能夠爲同一個對象多次執行此任務,並且每次「重複」答案都會簡單地覆蓋子鍵。請重新打開!

+5

1.用'.'分割它。2.使用'for'。 3. ??????? 4.盈利!!!! 111 – zerkms 2015-01-21 02:19:27

+0

哈是的所有關於'???????'部分 – tyler 2015-01-21 02:20:03

+0

Angular的['$ parse'](https://docs.angularjs.org/api/ng/service/ $解析)做得很好。也許看看[源代碼](https://github.com/angular/angular.js/blob/master/src/ng/parse.js#L956) – Phil 2015-01-21 02:20:42

回答

7

你在你的問題中提到lodash,所以我想我應該補充他們容易對象set()get()功能。只是這樣做:

_.set(record, 'availability_meta.supplier.price', 99); 

你可以閱讀更多關於它:https://lodash.com/docs#set

這些功能讓你做更復雜的事情太多,比如指定數組索引,等等:)

+1

燁,這是這裏最好的答案恕我直言..我用這個.. – 2015-10-03 21:37:28

+1

同意。雖然理解如何實現這樣的東西可能是數據結構中的一個很好的練習,但不必實施_並保持它自己幾乎總是一個優點。由於OP明確提到已經使用lodash,所以這看起來應該是被接受的答案。 – codermonkeyfuel 2016-01-23 04:42:04

8

的東西,讓你開始:

function assignProperty(obj, path, value) { 
    var props = path.split(".") 
     , i = 0 
     , prop; 

    for(; i < props.length - 1; i++) { 
     prop = props[i]; 
     obj = obj[prop]; 
    } 

    obj[props[i]] = value; 

} 

假設:

var arr = ["foo.bar.baz", 1]; 

你會調用它使用:

assignProperty(record, arr[0], arr[1]); 

例子:http://jsfiddle.net/x49g5w8L/

+0

請注意,您在調用assignProperty時缺少一個參數。 – ianaya89 2015-01-21 02:33:16

+1

@ ianaya89:謝謝,糾正。 – 2015-01-21 02:33:44

+0

@PastorBones:我回滾了你的編輯。它的寫法與設計一致。這很微妙,但是它變成'length-1'的原因是因爲循環結束後,'i'等於最後一個屬性(在本例中爲'baz')。因此,函數中的最後一行將值賦給正確的屬性。 – 2016-04-29 17:19:35

2

只要做

record['foo.bar.baz'] = 99; 

但是,這將如何工作?這是嚴格的冒險V8環境(鉻或節點和諧),使用Object.observe。我們觀察對象並捕獲新屬性的添加。當添加「屬性」foo.bar.baz(通過賦值)時,我們檢測到這是一個虛線屬性,並將其轉換爲record['foo']['bar.baz'](如果它不存在,則創建record['foo']),然後轉換爲賦值給record['foo']['bar']['baz']。它是這樣的:

function enable_dot_assignments(changes) { 

    // Iterate over changes 
    changes.forEach(function(change) { 

     // Deconstruct change record. 
     var object = change.object; 
     var type = change.type; 
     var name = change.name; 

     // Handle only 'add' type changes 
     if (type !== 'add') return; 

     // Break the property into segments, and get first one. 
     var segments = name.split('.'); 
     var first_segment = segments.shift(); 

     // Skip non-dotted property. 
     if (!segments.length) return; 

     // If the property doesn't exist, create it as object. 
     if (!(first_segment in object)) object[first_segment] = {}; 

     var subobject = object[first_segment]; 

     // Ensure subobject also enables dot assignments. 
     Object.observe(subobject, enable_dot_assignments); 

     // Set value on subobject using remainder of dot path. 
     subobject[segments.join('.')] = object[name]; 

     // Make subobject assignments synchronous. 
     Object.deliverChangeRecords(enable_dot_assignments); 

     // We don't need the 'a.b' property on the object. 
     delete object[name]; 
    }); 
} 

現在,你可以做

Object.observe(record, enable_dot_assignments); 
record['foo.bar.baz'] = 99; 

但請注意,這樣的分配將是異步,這可能會或可能不會爲你工作。要解決此問題,請在作業後立即致電Object.deliverChangeRecords。或者,雖然不如語法賞心悅目,你可以寫一個輔助功能,還設置了觀察:

function dot_assignment(object, path, value) { 
    Object.observe(object, enable_dot_assignments); 
    object[path] = value; 
    Object.deliverChangeRecords(enable_dot_assignments); 
} 

dot_assignment(record, 'foo.bar.baz', 99); 
1

事情是這樣的例子也許。它將擴展提供的對象或創建一個,如果它沒有提供對象。如果你提供已經存在於對象中的鍵,它本質上是破壞性的,但如果這不是你想要的,你可以改變它。使用ECMA5。

/*global console */ 
 
/*members split, pop, reduce, trim, forEach, log, stringify */ 
 
(function() { 
 
    'use strict'; 
 

 
    function isObject(arg) { 
 
     return arg && typeof arg === 'object'; 
 
    } 
 

 
    function convertExtend(arr, obj) { 
 
     if (!isObject(obj)) { 
 
      obj = {}; 
 
     } 
 

 
     var str = arr[0], 
 
      last = obj, 
 
      props, 
 
      valProp; 
 

 
     if (typeof str === 'string') { 
 
      props = str.split('.'); 
 
      valProp = props.pop(); 
 
      props.reduce(function (nest, prop) { 
 
       prop = prop.trim(); 
 
       last = nest[prop]; 
 
       if (!isObject(last)) { 
 
        nest[prop] = last = {}; 
 
       } 
 

 
       return last; 
 
      }, obj); 
 

 
      last[valProp] = arr[1]; 
 
     } 
 

 
     return obj; 
 
    } 
 

 
    var x = ['fum'], 
 
     y = [ 
 
      ['foo.bar.baz', 1], 
 
      ['foo.bar.fum', new Date()], 
 
      ['qux.qaz', 'abc'], 
 
      ['foshizzle', 200] 
 
     ], 
 
     z = ['qux.qux', null], 
 
     record = convertExtend(x); 
 

 
    y.forEach(function (yi) { 
 
     convertExtend(yi, record); 
 
    }); 
 

 
    convertExtend(z, record); 
 
    document.body.textContent = JSON.stringify(record, function (key, value, Undefined) { 
 
     /*jslint unparam:true */ 
 
     /*jshint unused:false */ 
 
     if (value === Undefined) { 
 
      value = String(value); 
 
     } 
 

 
     return value; 
 
    }); 
 
}());

1

這個什麼?

function convertDotPathToNestedObject(path, value) { 
    const [last, ...paths] = path.split('.').reverse(); 
    return paths.reduce((acc, el) => ({ [el]: acc }), { [last]: value }); 
} 

convertDotPathToNestedObject('foo.bar.x', 'FooBar') 
// { foo: { bar: { x: 'FooBar' } } } 
+0

我正在使用GraphQL,這接近於我需要使用深度嵌套更新的事情。 請參閱:https://medium.com/pro-react/a-brief-talk-about-immutability-and-react-s-helpers-70919ab8ae7c – 2017-04-18 16:52:04

相關問題