假设我有以下数据
var obj = {
test: 'somedata',
scores: [
{
"points":99,
"id":"x12"
},
{
"points":21,
"id":"x13"
}
],
sites: [
{
"exercises":21,
"sid":"s12"
},
{
"exercises":23,
"sid":"s11"
}
],
history: {
key: 'value',
commits: [
{
id: 1,
value: 'thank you'
}
]
}
}
请注意scores
和sites
包含基于 id
且具有唯一元素的数组在scores
并基于sid
在sites
。我想要一个具有以下魔力的函数:
//will **update** obj.test to 'newdata' and return {test:'newdata'}
magicUpdate(obj, {test:'newdata'})
//will **insert** obj.newkey with with value 'value' and return {newkey: 'value'}
magicUpdate(obj, {newkey: 'value'})
//will do nothing and return {}
magicUpdate(obj, {scores: []})
//will **update** scores[0] and return {scores:[{points:3, id: "x12"}]}, as id "x12" is already in the array at index 0
magicUpdate(obj, {scores:[{points:3, id: "x12"}])
//will **insert** {points:3, id: "x14"} into obj.scores and return {scores:[{points:3, id: "x14"}]}
magicUpdate(obj, {scores:[{points:3, id: "x14"}]})
//will **update** sites[0] and return {sites:[{exercises:22, sid: "s12"}]}, as id "s12" is already in the array at index 0
magicUpdate(obj, {sites:[{exercises:22, sid: "s12"}])
//will **insert** {exercises:10, sid: "s14"} into obj.sites and return {sites:[{exercises:10, sid: "s14"}]}
magicUpdate(obj, {sites:[{exercises:10, sid: "s14"}]})
//and also be recursive ...
//will **update** obj.history.commits[0]
magicUpdate(obj, {'history.commits': [{id:1, value: 'changed'}]});
我见过.update 进行递归,但前提是传递 path
这应该是自动确定的。然后是 .merge其内部使用 _.baseMerge尽管我不理解该函数的签名,但它非常接近我所需要的。
_.merge(
{scores:[{id: 12, points:10}, {id: 13, points:10}]},
{scores:[{id: 14, points:10}, {id: 15, points:10}]}
)
// returns {scores:[{id: 14, points:10}, {id: 15, points:10}]} not the fully merged array
有人能给我指出一个好的方向或者用 lodash 实现了类似的事情吗?
最佳答案
您在帖子中提到的 magicUpdate
函数确实可以使用 lodash 函数来实现。
对于这个实现,我主要使用了 _ .get 、 _ .set 和 _ .unionWith ,尽管我确信它可以使用其他一些来实现:
// src will be mutated. For simplicity's sake, obj is an object with only one property that represents the changes to make to src
function magicUpdate(src, obj) {
var key = _.first(_.keys(obj)),
value = _.get(obj, key),
srcValue = _.get(src, key),
comparator = function(a, b) {
var idKey = _.isUndefined(a.id) ? 'sid' : 'id';
return a[idKey] === b[idKey];
}
if (_.isArray(srcValue)) {
value = _.unionWith(value, srcValue, comparator);
}
return _.set(src, key, value);
}
正如您可能已经注意到查看代码一样,返回类型是变异对象,而不是您所要求的类型。我不太确定您想要什么返回值。
无论如何,Lodash 没有内置的对象差异函数,因此有必要开发类似的东西,以防万一你想要旧对象和修改后的对象之间的差异(你还必须 _ .clone对象首先要有一个副本并能够进行比较)。
我提出的函数的想法是尝试获取 obj 的键(这是我们要在 src 中修改的键)并检查它是否存在并且是一个数组。如果是这样,我们只需添加两个数组,并更新 src
中与 obj
中具有相同 id
的数组。由于 sites
、scores
和 history
有 id
和 sid
我必须向 _.unionWith
函数的比较器添加更多逻辑。
如果key
不存在或者不是数组,我们只需在src
中设置它。
如果您想使用它,这里有 fiddle。希望对您有所帮助。
更新
我的第一个解决方案旨在一次更新一个属性。不过,似乎可以同时更新多个。
一种快速解决方案可能是使用更新迭代对象并一次更新一个属性。
function updateProperty(src, obj) {
var key = _.first(_.keys(obj)),
value = _.get(obj, key),
srcValue = _.get(src, key),
comparator = function(a, b) {
var idKey = _.isUndefined(a.id) ? 'sid' : 'id';
return a[idKey] === b[idKey];
}
if (_.isArray(srcValue)) {
value = _.unionWith(value, srcValue, comparator);
}
return _.set(src, key, value);
}
function magicUpdate(obj, src) {
_.forEach(src, function(value, key) {
updateProperty(obj, _.pick(src, key));
});
return obj;
}
关于javascript - 更新 Javascript 对象、嵌套数据 - 仅在不可更新时插入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40041787/