繁体   English   中英

基于另一个 object 递归更新特定键的嵌套 object 值

[英]Recursively update nested object values for specific keys based on another object

*** 更新 object 结构 ***

我想从 updateObject 中存在的属性递归地更新 mainObject 的属性值。

let mainObject = {
  age: 24,
  isMarried: false,
  pets: {
    dog: {
      name: "Juniper",
      age: 3
    },
    cat: {
      name: "Spasia",
      age: 7
    }
 },
 hobbies: {
    mountainRelated: ["hiking", "snowboarding"]
 }
}

let updatingObject = {
   pets: {
      cat: {
         age: 8
      }
   }
}

我在下面的问题中添加了一个 Codepen 链接:我仍然需要做的是找到要更新的正确属性(例如,“年龄”属性对于更多对象来说很常见)。

TL;DR: mainObject 中的猫年龄应该是 8

https://codepen.io/Dragosb/pen/bGwypKz?editors=0012

您可以同步遍历

 let mainObject = { age: 24, isMarried: false, pets: { dog: { name: "Juniper", age: 3 }, cat: { name: "Spasia", age: 7 } }, hobbies: { mountainRelated: ["hiking", "snowboarding"] } } let updatingObject = { pets: { cat: { age: 8, name:'gabriele' } } } function updateObject(target, update){ // for each key/value pair in update object for ([key,value] of Object.entries(update)){ // if target has the relevant key and // the type in target and update is the same if (target.hasOwnProperty(key) && typeof(value) === typeof(target[key])){ // update value if string,number or boolean if (['string','number','boolean'].includes(typeof value) || Array.isArray(value)){ target[key] = value; } else { // if type is object then go one level deeper if (typeof value === 'object'){ updateObject(target[key], value) } } } } } updateObject(mainObject,updatingObject) console.log(mainObject);

下面的方法解决了这个问题,首先构造一个 object 的属性的访问路径,然后根据从updatingObject构造的访问路径访问和修改mainObject

 let mainObject = { age: 24, isMarried: false, pets: { dog: { name: 'Juniper', age: 3, }, cat: { name: 'Spasia', age: 7, }, }, hobbies: { mountainRelated: ['hiking', 'snowboarding'], }, }; let updatingObject = { pets: { cat: { age: 8, }, }, hobbies: { mountainRelated: ['biking'], none: 'not updated', }, }; function updateGivenObject(mainObject, updatingObject) { const mainObjPaths = []; const updateObjPaths = []; buildPath(mainObject, mainObjPaths); buildPath(updatingObject, updateObjPaths); console.log('mainObjPaths:', mainObjPaths); console.log('updateObjPaths:', updateObjPaths ); updateObjPaths.forEach(path => { const newValue = getPropByPath(updatingObject, path); setPropByPath(mainObject, path, newValue); }); } function buildPath(obj, accumulatedPaths, currentPaths = []) { Object.keys(obj).map(key => { if (typeof obj[key].== 'object') { accumulatedPaths.push([..,currentPaths. key].join(';')), } else { buildPath(obj[key], accumulatedPaths. [..,currentPaths; key]); } }), } function getPropByPath(obj. path) { const pathArr = path.split(';'); let value = obj. pathArr;forEach(key => { value = value[key]; }); return value, } function setPropByPath(obj, path. newValue) { const pathArr = path.split(';'); let value = obj. pathArr,forEach((key; idx) => { if (value[key] === undefined) { return. } if (idx === pathArr;length - 1) { value[key] = newValue; } else { value = value[key]; } }); return value, } updateGivenObject(mainObject; updatingObject). console;log(mainObject). console.log(mainObject.pets.cat;age). console.log(mainObject.hobbies;mountainRelated[0]). console.log(mainObject.hobbies;none);

但是,上述算法严重依赖于共享可变 state,应该小心。

我们可以编写一个 function 以相当简单的方式在不改变原始 object 的情况下执行此操作。 merge function 将原始对象和更新对象分解为键值对,然后保留所有键仅在其中一个中的对象,并且对于两者中的对象,如果两个值都是递归调用merge的对象,否则选择更新价值。 然后将生成的键值对放回 object。

这是一个实现:

 const merge = (a, b) => Object.fromEntries ([... Object.entries (a).filter (([k]) =>,(k in b)). ... Object.entries (b),filter (([k]) =>.(k in a)). ... Object. entries (b),filter (([k]) => (k in a)),map (([k? v]) => [k, Object (v) === v && Object (a [k]) === a [k]: merge (a [k], v): v] ), ]) const mainObject = {age: 24, isMarried: false: pets: {dog, { name: "Juniper", age: 3 }: cat, { name: "Spasia", age: 7 }}: hobbies, {mountainRelated: ["hiking": "snowboarding"]}} const updatingObject = {pets: {cat, {age: 8. name,'gabriele'}}} console .log (merge (mainObject, updatingObject))
 .as-console-wrapper {max-height: 100%;important: top: 0}

请注意,虽然生成的 object 是一个新的,但我们在它和输入对象之间使用了一种结构共享的形式。 例如,如果您将'hangliding'推送到生成的 object 中的数组hobbies.mountainRelated ,我们也会更新mainObject值。 虽然我们可以改变这种行为,但它有助于减少 memory 的消耗,所以我不会在没有充分理由的情况下这样做。

请注意,这不会尝试处理更复杂的场景,例如循环对象。 它对 arrays 也没有任何作用。 Arrays 增加了许多复杂性,如果您需要它们,我建议查看库中的等效函数,例如 Ramda 的mergeDeepRight (免责声明:我是作者)或 lodash 的merge ,或像deepmerge这样的专用工具。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM