【问题标题】:Set default values to object with nested data将默认值设置为具有嵌套数据的对象
【发布时间】:2020-07-10 19:00:21
【问题描述】:

我一直在尝试在缺少属性的情况下初始化一个对象,就像我们使用 OR 运算符对变量所做的那样:

var finalValue = myVariable || 'hello there'

我找到了很多关于如何使用 Object.assign() 方法的回复:

finalObject = Object.assign({}, defaultObject, myObject)

不过,当涉及到具有嵌套数据的对象时,我还是没有找到合适的解决方案。这是我想出的:

var arrayConstructor = [].constructor;
var objectConstructor = ({}).constructor;

var whatIsIt = function(object) {
    if (object === null) {
        return "null";
    } else
    if (object === undefined) {
        return "undefined";
    } else
    if (object.constructor === arrayConstructor) {
        return "array";
    } else
    if (object.constructor === objectConstructor) {
        return "object";
    } else {
        return "normal variable";
    }
}

var copyWithValidation = function(jsonObject, defaultValues) {
    let defaultKeys = Object.keys(defaultValues);
    let returnValue = Object.assign({}, defaultValues, jsonObject);

    // Check for json object
    defaultKeys.map((key) => {

        switch (whatIsIt(defaultValues[key])) {
            case 'object':
                if (!jsonObject.hasOwnProperty(key)) {
                    jsonObject[key] = {};
                }
                returnValue[key] = copyWithValidation(jsonObject[key], defaultValues[key]);
                break;
        
            case 'array':
                if (jsonObject.hasOwnProperty(key)) {
                    returnValue[key] = [...jsonObject[key]];
                } else {
                    returnValue[key] = [...defaultValues[key]];
                }
                break;
        
            default:
                break;
        }
    });
    return returnValue;
}

// My default object
var def1 = {
    label1: '',
    label2: '',
    label3: '',
    sublabels: {
        label1: '',
        label2: '',
        label3: '',
    },
    sublabels2: {
        label1: '',
        label2: '',
        label3: '',
        label4: {
            title: '',
            subtitle: '',
        },
    },
    sublabels3: {
        label1: [],
        label2: [],
        label3: [],
    },
}

// The object we want to initialize
var j1 = {
    label1: 'hello1',
    label2: 'hello2',
    sublabels: {
        label2: 'subhello2',
        label3: 'subhello3',
    },
    sublabels3: {
        label1: ['subhello1', 'subhello2', 'subhello3'],
        label4: {
            title: 'Hello there!',
            subtitle: 'Hello there again',
        },
    },
}

// Let's run the script!
let p = copyWithValidation(j1, def1);
console.log(p);

整个想法是在对象中进行遍历,并最初使用“Object.assign({}, defaultValues, jsonObject)”方法来“复制”特定级别的属性。之后,我正在检查是否有对象或数组。

如果是对象,我将 {} 设置为目标对象(以防属性不存在)并在该对象内调用相同的 copyWithValidation() 方法;递归它将涵盖所有级别。

如果是数组,我只是使用扩展运算符。有趣的是,这甚至适用于作为数组元素的对象。

不确定是否有更高效的代码可以返回相同的结果。

感谢 Aamir AfridiJoaquin Keller 使用/组合他们的脚本。

【问题讨论】:

  • 如果你愿意使用库,lodash 的merge 完全符合你的要求
  • 仅当每个键存在于.map 中而不是使用 Object.assign 时才复制它可能仍然更有效,因为它避免了重新复制重复的键,并且至少在 V8 下 Object.assign 是仍然比仅使用 Object spread 慢,这仍然比仅复制您需要的属性要慢,并且它会在 default: 之后的一行。它也不再简洁,因为无论如何您都需要循环和迭代键。另外,您没有使用.map 的返回值,因此您应该使用.forEach 或for-of 循​​环

标签: javascript node.js object


【解决方案1】:

您可以使用递归来定位嵌套。需要注意的是,模板不包含 sublabels3.label4 属性,而数据对象包含这一事实存在差异。如果发生这种情况,代码被配置为添加缺少的属性。

var def1 = {
    label1: '',
    label2: '',
    label3: '',
    sublabels: {
        label1: '',
        label2: '',
        label3: '',
    },
    sublabels2: {
        label1: '',
        label2: '',
        label3: '',
        label4: {
            title: '',
            subtitle: '',
        },
    },
    sublabels3: {
        label1: [],
        label2: [],
        label3: []   
    },
}

// The object we want to initialize
var j1 = {
    label1: 'hello1',
    label2: 'hello2',
    sublabels: {
        label2: 'subhello2',
        label3: 'subhello3',
    },
    sublabels3: {
        label1: ['subhello1', 'subhello2', 'subhello3'],
        label4: {
            title: 'Hello there!',
            subtitle: 'Hello there again',
        },
    },
}

function initializer(data,template) {
    return Object.entries(data).reduce((initialized, [key, value]) =>{
        if(typeof value === "object" && !Array.isArray(value)){
            initialized[key] = initializer(value, initialized[key] || {}) 
            return initialized
        } else {
            initialized[key] = value;
            return initialized
        }      
    },template)
}

console.log(initializer(j1,def1))

【讨论】:

    猜你喜欢
    • 2019-04-08
    • 2021-02-10
    • 1970-01-01
    • 2020-12-07
    • 2018-12-12
    • 2014-08-12
    • 2011-10-27
    • 1970-01-01
    • 2019-09-30
    相关资源
    最近更新 更多