[英]How to merge objects with the same properties into an Array?
I would like to merge 2 objects with the same properties into an Array.我想将具有相同属性的 2 个对象合并到一个数组中。
Take this for an example:以此为例:
object1 = {"id":1,
"name":name1,
"children":[{"id":2,"name":name2}]
};
object2 = {"id":3,
"name":name3,
"children":[{"id":4,"name":name4}]
};
object3 = {"id":1,
"name":name1,
"children":[{"id":6,"name":name6}]
};
var result = Object.assign(result,object1,object2,object3);
Expected result:预期结果:
JSON.stringify([result]) =[
{"id":1,
"name":name1,
"children":[{"id":2,"name":name2},
{"id":6,"name":name6}]
},
{"id":3,
"name":name3,
"children":[{"id":4,"name":name4}]
}
]
Actual result:实际结果:
JSON.stringify([result]) = [
{"id":3,
"name":name3,
"children":[{"id":4,"name":name4}]
}
]
Seems like Object.assign() isn't the way to go... as it will overwrite, I do not want it to overwrite, I want them to merge instead.似乎 Object.assign() 不是要走的路……因为它会覆盖,我不希望它覆盖,我希望它们合并。 Is there a right way to do this?
有正确的方法吗?
As so often, Array.prototype.reduce
provides a good base for an approach like eg this one ...通常,
Array.prototype.reduce
为像这样的方法提供了一个很好的基础......
var obj1 = { "id": 1, "name": "name1", "children": [{ "id": 2, "name": "name2" }] }; var obj2 = { "id": 3, "name": "name3", "children": [{ "id": 4, "name": "name4" }] }; var obj3 = { "id": 1, "name": "name1", "children": [{ "id": 6, "name": "name6" }] }; // Expected result: [{ // "id": 1, // "name": name1, // "children": [ // { "id": 2, "name": "name2" }, // { "id": 6, "name": "name6" } // ] // }, { // "id": 3, // "name": "name3", // "children": [{"id": 4, "name": "name4" }] // }] function mergeEquallyLabeledTypes(collector, type) { var key = (type.name + '@' + type.id); // identity key. var store = collector.store; var storedType = store[key]; if (storedType) { // merge `children` of identically named types. storedType.children = storedType.children.concat(type.children); } else { store[key] = type; collector.list.push(type); } return collector; } var result = [obj1, obj2, obj3].reduce(mergeEquallyLabeledTypes, { store: {}, list: [] }).list; console.log('result : ', result);
.as-console-wrapper { max-height: 100%!important; top: 0; }
Edit Note编辑备注
After having been informed about changed requirements, that need to deal with a nested pattern, I will change my first provided approach into a generic solution.在被告知需要处理嵌套模式的更改需求后,我会将我提供的第一个方法更改为通用解决方案。 It will be not that difficult since there is a generically repeated pattern within the data structure.
这不会那么困难,因为在数据结构中存在一般重复的模式。 Thus I just need to make the already existing reducer function self recursive.
因此,我只需要使已经存在的 reducer 函数自递归。 A recursion step will be triggered after having finished a complete reducing cycle on any provided list ...
在任何提供的列表上完成一个完整的归约循环后,将触发一个递归步骤......
var obj1 = { "id": 1, "name": "name1", "children": [{ "id": 2, "name": "name2", "children": [{ "id": 8, "name": "name8" }] }] }; var obj2 = { "id": 3, "name": "name3", "children": [{ "id": 4, "name": "name4", "children": [{ "id": 9, "name": "name9" }] }] }; var obj3 = { "id": 1, "name": "name1", "children": [{ "id": 6, "name": "name6", "children": [{ "id": 10, "name": "name10" }] }] }; var obj4 = { "id": 3, "name": "name3", "children": [{ "id": 4, "name": "name4", "children": [{ "id": 11, "name": "name11" }] }] }; function mergeEquallyLabeledTypesRecursively(collector, type, idx, list) { var key = (type.name + '@' + type.id); // identity key. var store = collector.store; var storedType = store[key]; if (storedType) { // merge `children` of identically named types. storedType.children = storedType.children.concat(type.children); } else { store[key] = type; collector.list.push(type); } // take repetitive data patterns into account ... if (idx >= (list.length - 1)) { collector.list.forEach(function (type) { // ... behave recursive, when appropriate. if (type.children) { type.children = type.children.reduce(mergeEquallyLabeledTypesRecursively, { store: {}, list: [] }).list; } }); } return collector; } var result = [obj1, obj2, obj3, obj4].reduce(mergeEquallyLabeledTypesRecursively, { store: {}, list: [] }).list; console.log('result : ', result);
.as-console-wrapper { max-height: 100%!important; top: 0; }
This might be what your after, please note it's
not recursive
now recursive.这可能是你之后的事情,请注意它
不是递归
的,现在是递归的。 But your example data doesn't appear to be anyway.但是您的示例数据似乎并非如此。
const object1 = {"id":1, "name":"name1", "children":[{"id":2,"name":"name2"}] }; const object2 = {"id":3, "name":"name3", "children":[{"id":4,"name":"name4"}] }; const object3 = {"id":1, "name":"name1", "children":[ {"id":6,"name":"name6"}, {"id":7,"name":"name7"}, {"id":6,"name":"name6"} ] }; function merge(arr) { const idLinks = {}; const ret = []; arr.forEach((r) => { if (!idLinks[r.id]) idLinks[r.id] = []; idLinks[r.id].push(r); }); Object.keys(idLinks).forEach((k) => { const nn = idLinks[k]; const n = nn[0]; for (let l = 1; l < nn.length; l ++) { if (nn[l].children) { if (!n.children) n.children = []; n.children = n.children.concat(nn[l].children); } } if (n.children && n.children.length) n.children = merge(n.children); ret.push(n); }); return ret; } var result = merge([object1,object2,object3]); console.log(result);
In functional programming way with es6 standards.以 es6 标准的函数式编程方式。 I am assuming children array also contains duplicates.
我假设 children 数组也包含重复项。 I enclosed the code in closures.
我将代码封闭在闭包中。
See the following link why I used util
to print all the object in node console.log()
请参阅以下链接为什么我使用
util
打印节点console.log()
中的所有对象
How can I get the full object in Node.js's console.log(), rather than '[Object]'? 如何在 Node.js 的 console.log() 中获取完整的对象,而不是 '[Object]'?
(function() {
'use strict';
const util = require('util');
/** string constants */
const ID = 'id';
const CHILDREN = 'children';
/* Objects to modify */
const object1 = {
"id": 1,
"name": "name1",
"children": [
{ "id": 2, "name": "name2" },
{ "id": 5, "name": "name5" },
{ "id": 7, "name": "name7" }
]
};
const object2 = {
"id": 3,
"name": "name3",
"children": [
{ "id": 4, "name": "name4" }
]
};
const object3 = {
"id": 1,
"name": "name1",
"children": [
{ "id": 5, "name": "name5" },
{ "id": 6, "name": "name6" }
]
};
/**
* Concates the arrays
* @param { array } - a
* @param { array } - b
*/
const merge = (a, b) => {
return a.concat(b);
};
/**
* Removes Duplicates from the given array based on ID
* @param { array } - array to remove duplicates
* @return { array } - array without duplicates
*/
const removeDuplicates = (arr) => {
return arr.filter((obj, pos, arr) => {
return arr.map((m) => {
return m[ID];
}).indexOf(obj[ID]) === pos;
});
}
/**
* Groups items in array with particular key
* Currying technique
* @param { prop } - key to group
* @return { () => {} } - Method which in turn takes array as argument
*/
const groupBy = (prop) => (array) => {
return array.reduce((groups, item) => {
const val = item[prop];
groups[val] = groups[val] || [];
groups[val].push(item);
return groups;
}, {});
}
/**
* Object containing grouped-items by particuar key
*/
const grouped = groupBy(ID)([object1, object2, object3]);
/**
* Removing the duplicates of children
* Remember map also mutates the array of objects key's value
* but not data type
*/
Object.keys(grouped).map((key, position) => {
grouped[key].reduce((a, b) => {
a[CHILDREN] = removeDuplicates(a[CHILDREN].concat(b[CHILDREN]));
});
});
/**
* Desired final output
*/
const final = Object.keys(grouped)
.map((key) => removeDuplicates(grouped[key]))
.reduce(merge, []);
console.log(util.inspect(final, false, null))})();
/* There are two cases :
a) No duplicate children
b) Duplicate children either in (same object || different object|| both)
*/
/* =============== */
/* Case a) */
const util = require('util');
var object1 = {
"id": 1,
"name": "name1",
"children": [{ "id": 2, "name": "name2" }]
};
var object2 = {
"id": 3,
"name": "name3",
"children": [{ "id": 4, "name": "name4" }]
};
var object3 = {
"id": 1,
"name":"name1",
"children":[{"id":6,"name":"name6"}]
};
var arr = [object1,object2,object3];
var uniqueIds = [];
var filteredArray = [];
var uniqueId='';
arr.map((item,i,array)=>{
uniqueId =uniqueIds.indexOf(item.id);
uniqueId = uniqueId+1;
uniqueIds = [...uniqueIds,item.id];
if(!uniqueId){
filteredArray[i] = item;
}
if(uniqueId){
filteredArray[uniqueId-1]['children'] = [...(array[uniqueId-1].children),...(item.children)];
}
});
console.log(util.inspect(filteredArray,false,null));
/* ============================================
Case b)
Dealing with the worst case of having duplicate children in both same
and different objects
*/
object1 = {"id":1,
"name":'name1',
"children":[{"id":2,"name":'name2'},
{"id":2,"name":'name2'}]
};
object2 = {"id":3,
"name":'name3',
"children":[{"id":4,"name":'name4'}]
};
object3 = {"id":1,
"name":'name1',
"children":[{"id":6,"name":'name6'},
{"id":7,"name":'name7'},
{"id":2,"name":'name2'}]
};
arr = [object1,object2,object3];
uniqueIds = [];
uniqueId = '';
arr.map((item,i,array)=>{
uniqueId =uniqueIds.indexOf(item.id);
uniqueId = uniqueId+1;
uniqueIds = [...uniqueIds,item.id];
if(!uniqueId){
filteredArray[i] = item;
}
if(uniqueId){
filteredArray[uniqueId-1]['children'] = [...(array[uniqueId-1].children),...(item.children)];
}
/*Removing duplicate children entries*/
filteredArray[uniqueIds.indexOf(item.id)]['children'] = filteredArray[uniqueIds.indexOf(item.id)]['children']
.filter((elem, index, self) => self.findIndex((t) => {return t.id === elem.id}) === index)
})
console.log(util.inspect(filteredArray,false,null));
const object1 = {
"id":1,
"name":"name1",
"children":[{"id":2,"name":"name2"}]
};
const object2 = {
"id":3,
"name":"name3",
"children":[{"id":4,"name":"name4"}]
};
const object3 = {
"id":1,
"name":"name1",
"children":[{"id":6,"name":"name6"}]
};
var array = [object1,object2,object3];
var array2 = [object1,object2,object3];
function uniquearray(obj){
var result =[];
for(var i=0;i<array.length;i++){
if(obj.id == array[i].id){
result.push(array[i])
array.splice(i,1)
}
}
return result;
}
var arrayofuniarrays = []
for(var i=0;i<array2.length;i++){
arrayofuniarrays.push(uniquearray(array2[i]))
}
for(var i=0;i<arrayofuniarrays.length;i++){
for(var j=1;j<arrayofuniarrays[i].length; j++){
arrayofuniarrays[i][0].children.push(arrayofuniarrays[i][j].children)
arrayofuniarrays[i].splice(j,1)
}
}
var resul = arrayofuniarrays.reduce(function(a, b){return a.concat(b)},[])
console.log(resul)
Here is a sketch example of how to do this.这是如何执行此操作的草图示例。 It leverages a mapped type using your
id
as a key to ensure each item only appears once.它利用使用您的
id
作为键的映射类型来确保每个项目只出现一次。 It adds all of the children to an array based on the id.它将所有子项添加到基于 id 的数组中。
If you needed to enforce the same behaviour on the children, you could use the same technique.如果您需要对孩子强制执行相同的行为,您可以使用相同的技术。
I have split this into multiple iterations to show you the individual parts in play.我已将其拆分为多个迭代,以向您展示正在运行的各个部分。
Usually, it is more efficient to avoid creating objects that need to be zipped back up if you can.通常,如果可以的话,避免创建需要压缩备份的对象会更有效。
const object1 = { "id": 1, "name": "name1", "children": [{ "id": 2, "name": "name2" }] }; const object2 = { "id": 3, "name": "name3", "children": [{ "id": 4, "name": "name4" }] }; const object3 = { "id": 1, "name":"name1", "children":[{"id":6,"name":"name6"}] }; const all = [object1, object2, object3]; // Use a map like a dictionary to enforce unique keys const mapped = {}; for (let obj of all) { if (!mapped[obj.id]) { mapped[obj.id] = obj; continue; } mapped[obj.id].children.push(obj.children); } console.log('Mapped ==> '+JSON.stringify(mapped)); // If you want to convert the mapped type to an array const result = []; for (let key in mapped) { result.push(mapped[key]); } console.log('Array ==> '+JSON.stringify(result));
Building on @Peter Seliger's answer here , I derived with the following method to merge arrays with deeply nested children.在@Peter Seliger's answer here的基础上,我使用以下方法派生了将数组与深度嵌套的子级合并。
Given the following objects:给定以下对象:
var obj1 = {
"id": 1,
"name": "name1",
"children": [{ "id": 2, "name": "name2", children:[{ "id":8, "name": "name8" }] }]
};
var obj2 = {
"id": 3,
"name": "name3",
"children": [{ "id": 4, "name": "name4", children:[{ "id":9, "name": "name9" }] }]
};
var obj3 = {
"id": 1,
"name": "name1",
"children": [{ "id": 6, "name": "name6", children:[{ "id":10, "name": "name10" }] }]
};
var obj4 = {
"id": 3,
"name": "name3",
"children": [{ "id": 4, "name": "name4", children:[{ "id":11, "name": "name11" }] }]
};
First we merge the parents首先我们合并父母
function mergeEquallyLabeledTypes(collector, type) {
var key = (type.name + '@' + type.id); // identity key.
var store = collector.store;
var storedType = store[key];
if (storedType) { // merge `children` of identically named types.
if(storedType.children)
storedType.children = storedType.children.concat(type.children);
} else {
store[key] = type;
collector.list.push(type);
}
return collector;
}
var result = [obj1, obj2, obj3, obj4].reduce(mergeEquallyLabeledTypes, {
store: {},
list: []
}).list;
Then we merge the children and subchildren if any.然后我们合并孩子和子孩子(如果有的话)。
for(let i=0; i<result.length; i++){
var children = result[i].children;
if(children){
var reducedChildren = children.reduce(mergeEquallyLabeledTypes, {store: {}, list: []}).list;
for(let j=0; j<reducedChildren.length; j++){
var subchildren = reducedChildren[j].children;
if(subchildren){
var reducedSubchildren = subchildren.reduce(mergeEquallyLabeledTypes, {store: {}, list: []}).list;
reducedChildren[j].children = reducedSubchildren;
}
}
result[i].children = reducedChildren;
}
}
Finally the result will be what I'll parse into my website.最后结果将是我将解析到我的网站中的内容。
console.log('result : ', result);
I am able to get the expected result.我能够得到预期的结果。
// result: [{
// "id": 1,
// "name": name1,
// "children": [
// { "id": 2, "name": "name2", children:[{ "id":8, "name": "name8" }] },
// { "id": 6, "name": "name6", children:[{ "id":10, "name": "name10" }] }
// ]
// }, {
// "id": 3,
// "name": "name3",
// "children": [{"id": 4, "name": "name4", children:[
// { "id":9, "name": "name9" },
// { "id":11, "name": "name11" }
// ]
// }
// ]
// }]
However, this might not be too efficient as I'll need to keep adding on to the merging of children/subchildren method if my tree get nested with more levels.但是,这可能不太有效,因为如果我的树嵌套更多级别,我需要继续添加子/子子合并方法。 (eg subsubchildren, subsubsubchildren and so on...)
(例如 subsubchildren、subsubsubchildren 等等...)
Is there any more efficient way to do this?有没有更有效的方法来做到这一点?
const object1 = {
id:1,
name:'a',
}
const object2 = {
id:3,
name:'b',
}
const object3 = {
id:1,
name:'c',
}
const originArr = [object1, object2, object3]
const idArr = [object1.id, object2.id, object3.id]
const newIdArr = []
for (let id of idArr) {
if (newIdArr.indexOf(id)) newIdArr.push(id)
}
const result = newIdArr.map(id => {
let names = []
for (obj of originArr) {
if (id === obj.id) names.push(obj.name)
}
return { id, names }
})
console.log(result)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.