问题描述
如果我有这个 root
对象:
const root = {
list: {
first: false,second: false,third: false,forth: false
}
};
并且想要创建一个具有相同结构但更改单个变量的新对象。我可以通过以下方式做到这一点:
const result = {
...root,list: {
...root.list,first: true
}
}
所以结果将是:
{
"list": {
"first": true,"second": false,"third": false,"forth": false
}
}
一切正常,但我需要以动态方式做同样的事情。假设我传递了 root
对象、需要更新的属性的完整路径 "list.first"
作为字符串和一个值。我找到了解决方案:
const merge = require('lodash').merge;
const root = {
list: {
first: false,forth: false
}
};
function createObjectByPathAndValue(path,value) {
const keys = (path || '').split('.') || [];
function createObjectByPathAndValue(root,keys,value) {
if (keys.length === 1) {
root[keys[0]] = value;
} else {
const key = keys.splice(0,1);
root[key] = createObjectByPathAndValue({},value);
}
return root;
}
const result = createObjectByPathAndValue({},value);
return result;
}
const changes = createObjectByPathAndValue('list.first',true);
const result = merge({},root,changes);
console.log(JSON.stringify(result,null,2 ));
但是让我们同意它很麻烦,而且代码太多。我认为我没有使用现代 javascript 的所有可能性,它可以更简单地完成。我说得对吗?
附言必须创建一个新对象,即不允许改变现有对象(我需要它来更新 React 的状态)
解决方法
您可以深度克隆对象并更改值:
const {cloneDeep,toPath,set} = require('lodash');
const root = {
list: {
first: false,second: false,third: false,forth: false
}
};
const result = cloneDeep(root);
set(result,toPath('list.first'),true);
console.log(JSON.stringify(result,null,2));
这是一个例子(jsfiddle 不支持 require
):
const root = {
list: {
first: false,forth: false
}
};
const result = _.cloneDeep(root);
_.set(result,_.toPath('list.first'),2));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
我最终使用了 set
包中的 lodash/fn/set
函数,如 this answer
const setFp = require('lodash/fp/set');
const root = {
list: {
first: false,forth: false
}
};
const newRoot = setFp(`list.first`,true,root); //a new object will be created
阅读有关 lodash/fp 软件包的更多信息。