【问题标题】:How to denormalize array in JS如何在 JS 中对数组进行非规范化
【发布时间】:2019-10-27 08:35:29
【问题描述】:

我有一个如下形式的数据集

let data = [
  {
    "id": {
      "primary": "A1"
    },
    "msg": 1
  }, {
    "id": {
      "primary": "A1"
    },
    "msg": 2
  }, {
    "id": {
      "primary": "B2"
    },
    "msg": 3
  }
]

我想把它改成

newData = [
  {
    "id": {
      "primary": "A1"
    },
    "items": [
      { "msg": 1 },
      { "msg": 2 }
    ]
  },
  {
    "id": {
      "primary": "B2"
    },
    "items": [
      { "msg": 3 }
    ]
  }
]

我认为该方法类似于以下内容,但不确定在这种情况下如何检查 undefined 值。

let newData = [];
for (let i = 0; i < data.length; i++) {
  if (newData[i]['id']['primary'] === data[i]['id']) newData.push(data[i]['id'])
  else newData[i]['items'].push(data[i]['msg'])
}

如何转换原始数据集以合并具有匹配 primary id 的条目?

【问题讨论】:

  • JSON 是 JavaScript 对象表示法。它是一种表示 JavaScript 对象的字符串格式。我没有看到 JSON.parseJSON.stringify 或任何其他表明您正在使用 JSON 的内容。

标签: javascript arrays denormalization


【解决方案1】:

一种选择是使用.reduce() 从现有数组中创建一个新数组。

我添加了 cmets 来澄清一下。

let data = [ { "id": { "primary": "A1" }, "msg": 1 }, { "id": { "primary": "A1" }, "msg": 2 }, { "id": { "primary": "B2" }, "msg": 3 } ];

let result = data.reduce((out,item) => {
  let {id, ...items} = item;                      //Separate the "id" and "everything else"
  let existing = out.find(({id}) => id.primary == item.id.primary); 

  existing                                        //have we seen this ID already?
    ? existing.items.push(items)                  //yes - add the items to it
    : out.push({ id: {...id}, items: [items]});   //no - create it
    
  return out;
  }, []);
  
console.log(result);

几个注意事项:

  • 您可能注意到我使用id: {...id} 设置了ID,尽管id 已经是一个对象。这是因为使用现有的id 对象会创建一个引用,而{...id} 会创建一个浅副本

  • 我没有在任何地方指定msg 属性。相反,不是id 的任何属性都将添加到items 列表(下面的示例)

        let data = [ { "id": { "primary": "A1" }, "msg": 1, "otherStuff": "Hello World!" }, { "id": { "primary": "A1" }, "msg": 2, "AnotherThing": true }, { "id": { "primary": "B2" }, "msg": 3, "someOtherProperty": false } ];
    
        let result = data.reduce((out,item) => {
          let {id, ...items} = item;
          let existing = out.find(({id}) => id.primary == item.id.primary); 
    
          existing
            ? existing.items.push(items)
            : out.push({ id: {...id}, items: [items]});
            
          return out;
          }, []);
          
        console.log(result);

    也就是说,如果您开始嵌套对象(ID 除外),它们可能会被包含为引用; ...items 只是一个 副本。

    如果出现这种情况,请考虑使用 JSON.parse(JSON.stringify(...)) 之类的内容进行深层复制。请务必阅读链接;有一些警告。

【讨论】:

  • 这看起来不错。感谢您的帮助!!我的实际数据结构有点复杂,所以我需要花一点时间来解决这个问题......
  • @Matthew 总的要点是您的 ID 将被复制到新对象中(如您在问题中所示)。其他所有内容都将被复制并集中到相应 ID 的 items 部分中。
  • 知道了。谢谢!! :)
【解决方案2】:

您也可以通过Array.reduceES6 destructuring 以简洁的方式解决这个问题:

let data = [ { "id": { "primary": "A1" }, "msg": 1 }, { "id": { "primary": "A1" }, "msg": 2 }, { "id": { "primary": "B2" }, "msg": 3 } ]

let result = data.reduce((r, {id, msg}) => 
  ((r[id.primary] = r[id.primary] || { id, items: [] }).items.push({msg}), r), {})

console.log(Object.values(result))

更易读的格式是:

let data = [ { "id": { "primary": "A1" }, "msg": 1 }, { "id": { "primary": "A1" }, "msg": 2 }, { "id": { "primary": "B2" }, "msg": 3 } ]

let result = data.reduce((r, {id, msg}) => {
  r[id.primary] = (r[id.primary] || { id, items: [] })
  r[id.primary].items.push({msg})
  return r
}, {})

console.log(Object.values(result))

想法是按id.primary 分组,然后一旦分组完成,只需通过Object.values 获取值

请注意,这是一种一次性解决方案,您不必每次迭代都针对当前累加器执行 Array.find

【讨论】:

  • {id, msg} 语法在这种情况下有什么作用?
  • 这只是 ES6 对象解构。我添加了指向我的答案的链接。
  • {id}, {msg} 是否等同?
  • 你太棒了。谢谢你:)
  • 值得注意的是,这是参考;更改data 中的属性或值也会更改result。当然,这可能不是真正的问题,具体取决于用例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-09-05
  • 2021-05-17
  • 2018-12-12
  • 2015-05-14
  • 2015-07-14
  • 2023-03-29
  • 2010-10-22
相关资源
最近更新 更多