如何在 JS 中对数组进行反规范化
How to denormalize array in JS
我有如下形式的数据集
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
的条目?
一种选择是使用 .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(...))
这样的深拷贝。不过请务必阅读 link;有一些注意事项。
你也可以通过 Array.reduce and ES6 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
。
我有如下形式的数据集
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
的条目?
一种选择是使用 .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(...))
这样的深拷贝。不过请务必阅读 link;有一些注意事项。
你也可以通过 Array.reduce and ES6 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
。