2015-01-21 61 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

相关问题