Javascript:将点分隔的字符串转换为嵌套的对象值

时间:2015-01-21 02:17:34

标签: javascript arrays object lodash

我有一堆对象属性以点分隔的字符串形式出现,例如"availability_meta.supplier.price",我需要为record['availability_meta']['supplier']['price']指定相应的值,依此类推。

并非所有东西都是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 个答案:

答案 0 :(得分: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/

答案 1 :(得分:8)

你在问题​​中提到了lodash,所以我想我应该添加他们的简单对象set()get()函数。做一些像:

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

您可以在此处详细了解:https://lodash.com/docs#set

这些函数也可以让你做更复杂的事情,比如指定数组索引等:)

答案 2 :(得分:3)

这个怎么样?

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' } } }

答案 3 :(得分:2)

只做

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

但这怎么会有用?对于使用Object.observe的V8环境(Chrome或节点和声)的冒险者而言,它是严格意义上的。我们观察对象并捕获新属性的添加。当添加“属性”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}}

答案 4 :(得分:1)

也许就像这个例子。如果没有提供对象,它将扩展提供的对象或创建一个对象。如果您提供对象中已存在的键,则它本质上是破坏性的,但如果不是您想要的,则可以更改它。使用ECMA5。

&#13;
&#13;
/*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;
    });
}());
&#13;
&#13;
&#13;