如何在JS中反规范化数组

时间:2019-06-12 19:02:27

标签: javascript arrays denormalization

我有以下形式的数据集

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的条目?

2 个答案:

答案 0 :(得分:3)

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

我添加了注释以澄清。

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对象将创建一个 reference ,而{...id}将创建一个浅的 copy

  • 我没有在任何地方指定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(...))之类的内容进行深层复制。不过请务必阅读链接;有一些警告。

答案 1 :(得分:1)

您还可以通过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