【问题标题】:Merge nested objects without mutating合并嵌套对象而不改变
【发布时间】:2016-11-24 07:33:12
【问题描述】:

我有以下 2 个 JavaScript 对象:

let o1 = {
  entities: {
    1: {
      text: "fooo",
      nested: {
        ids: [1, 2, 3],
        flag: true
      }
    }
  }
};

let o2 = {
  ids: [4, 5, 6]
}

我想合并它们而不改变它们,得到一个如下所示的对象:

let o3 = {
  entities: {
    1: {
      text: "fooo",
      nested: {
        ids: [1, 2, 3, 4, 5, 6],
        flag: true
      }
    }
  }
};

可能有 n 个entities,但只有entityId 定义的那个应该受到影响。 我尝试了什么:

let entityId = 1;

let o3 = Object.assign({}, o1, {
         entities: Object.assign({}, o1.entities, {
           [entityId]: Object.assign({}, o1.entities[entityId].nested,
            { ids: [...o1.entities[entityId].nested.ids, ...o2.ids] }
          )
        })
      });

问题是text: "fooo",nested: 完全消失了。

我的方法对吗?这段代码可以优化吗?

【问题讨论】:

  • "获取如下所示的对象:" - 您忘记包含示例输出。您是否正在为与 o1 相同的结构进行拍摄,但在数组中添加了额外的 id?
  • 对不起,我改了。没错
  • 您的示例输入有一个entities(复数)属性,但只有一个1 属性。真实数据是否有23等,如果有,o2的id应该与哪个合并?
  • 这个例子可以有更多的entities,只有变量entityId中的一组应该受到影响。
  • o1 合并到o3 然后将项目推入o3 数组?虽然对于实际用例来说可能太简单了,但是像这样链接 assign 根本不可读,恕我直言

标签: javascript ecmascript-6 redux


【解决方案1】:

我会这样做:

const merged = {
    entities: {
        [entityId]: {
            text: o1.entities[entityId].text,
            nested: {
                ids: [...o1.entities[entityId].nested.ids, ...o2.ids],
                flag: o1.entities[entityId].nested.flag
            }
        }
    }
}

【讨论】:

    【解决方案2】:

    由于您标记了 Redux,我假设您正在使用 React 并建议您使用不变性帮助器 - https://facebook.github.io/react/docs/update.html

    您的代码如下所示:

    let o3 = update(o1, {
        entities: {
            [entityId]: {
                nested: {
                    ids: {
                        $push: [4, 5, 6]
                    }
                }
            }
        }
    })
    

    ES6

    您的逻辑是正确的,但您的代码中缺少 nested 对象:

    let o3 = Object.assign({}, o1, {
         entities: Object.assign({}, o1.entities, {
           [entityId]: Object.assign({}, o1.entities[entityId], { 
              nested : Object.assign({}, o1.entities[entityId].nested ,{
                ids: [...o1.entities[entityId].nested.ids, ...o2.ids] }) 
            }
          )
        })
      });
    

    【讨论】:

    • 我正在使用Angular2,我会检查是否有这样的助手。提前致谢
    • @Daskus 无论如何,使用 Facebook 的助手可能没有什么问题,因为它是一个独立的模块 - facebook.github.io/immutable-js
    • 哦,你是对的!但是在将状态保存到 localStorage 时使用 Immutable.js 不是问题吗?
    • @Daskus 您只需要在存储/检索之前进行序列化/反序列化。
    • 我看不出这会有什么不同,因为 localStorage 将数据保存为字符串
    【解决方案3】:

    看起来您错过了一层合并 - 您的示例代码具有 entities.1.ids 而不是 entities.1.nested.ids。试试

    let entityId = 1;
    
    let o3 = Object.assign(
      {},
      o1,
      {
        entities: Object.assign(
          {},
          o1.entities,
          {
            [entityId]: Object.assign(
              {},
              o1.entities[entityId],
              Object.assign(
                {},
                o1.entities[entityId].nested,
                { ids: [...o1.entities[entityId].nested.ids, ...o2.ids] }
              )
            )
          }
        )
      }
    );
    

    此时,您可能希望从每个 Object.assign 调用中构建一些中间变量,以便更好地跟踪所有内容。

    【讨论】:

      【解决方案4】:

      嗯,你的第一个问题是这个:

      {
             [entityId]: Object.assign({}, o1.entities[entityId].nested,
      

      您将o1.entities[1].nested 的值复制到键o3.entities[1] 中,从而丢失了o1.entities[1] 中保存的对象中的其他键。

      这很难看到,因为正如其他 cmets 所指出的那样,这不是很漂亮的代码。

      当然,您想(并且打算)使用o1.entities[entityId]

      不过,您还有一个问题,如果您只对您当前感兴趣的 o1 部分进行 deep 复制。Object.assign() 将复制对象引用,而不是创建全新的对象。因此,如果您稍后修改o3.entities[1].text 的值,您也将改变o1.entities[1]

      【讨论】:

      • 我不这么认为,因为我将一个新的空对象作为第一个参数传递,该对象将被复制而不是变异:Object.assign({}, ...)
      • 当一个对象通过Object.assign() 复制时,它包含的任何本身是对象的值都只会被reference 复制。所以:
      • let a = {foo: {}};let b = Object.assign({}, a}; // Copies the contents of a into bb.bar = {}; // a is unaffected since b is a discrete copy of ab.foo.narf = 1; // a.foo is affected since it refers to the same object as b.fooconsole.log(a.foo); // prints "{narf: 1}"
      【解决方案5】:

      如果您知道您的数据不会包含任何日期或方法,您可以使用 JSON 深度克隆技巧:

      let o3 = JSON.parse(JSON.stringify(o1));
      Array.prototype.push.apply(o3.entities[1].nested.ids, o2.ids);
      

      【讨论】:

      • 在我的真实代码中我也有日期,所以你的方法不起作用......我试图简化代码。
      • 它实际上适用于日期,但日期对象(即不是时间字符串等)将被转换为 ISO 字符串。要将它们返回到日期对象,您可以简单地循环 o3(IMO 时间戳通常比日期对象更好,直到您仍然需要它)。除非您正在处理庞大的数据集,否则此方法将比尝试编写自己的解析器更清晰。
      • 或者查看 lodash 或 jquery.clone
      猜你喜欢
      • 2016-05-22
      • 1970-01-01
      • 2018-10-25
      • 1970-01-01
      • 1970-01-01
      • 2016-07-13
      • 2023-01-29
      • 2016-04-02
      • 1970-01-01
      相关资源
      最近更新 更多