合并 2 个对象数组,但如果属性具有相同的值则合并数组
Merge 2 arrays of objects but concat arrays if properties has the same value
我有两个具有下一个结构的对象:
let sourceObj = [
{
items: [
{ items: [{ id: '0', name: 'z' }], name: 'm' },
{ items: [{ id: '2', name: 'q' }], name: 'l' },
],
name: 'c'
},
{
items: [
{ items: [{ id: '4', name: '-' }], name: 'v' },
],
name: 'd'
}
];
let targetObj = [
{
items: [
{ items: [{ id: '1', name: 'd' }], name: 'm' },
{ items: [{ id: '3', name: 'b' }], name: 'j' },
],
name: 'c'
}
];
我想合并这个对象以获得一个具有下一个结构的对象:
let merged = [
{
items: [
{ items: [
{ id: '0', name: 'd' },
{ id: '1', name: 'z' }],
name: 'm'
},
{ items: [{ id: '2', name: 'q' }], name: 'l' },
{ items: [{ id: '3', name: 'b' }], name: 'j' },
],
name: 'c'
},
{
items: [
{ items: [{ id: '4', name: '-' }], name: 'v' },
],
name: 'd'
}
]
也就是我要获取对象,如果源数组的名称与目标数组中的名称相同,则该对象已连接数组。
我尝试使用 lodash 方法 mergeWith,但我只能加入集合的上层项目...
mergeWith(sourceObj, targetObj, (objValue, srcValue) => {
if (isArray(objValue)) {
return objValue.concat(srcValue);
}
});
你说得对,Lodash 的 mergeWith
不会递归合并。您可以自己手动管理。
- 先遍历第一个数组,通过
name
属性. 生成一个map对象
- 然后遍历第二个数组并检查是否
name
属性
匹配,如果匹配则递归调用merge
函数进行合并
来自源数组和目标数组的两个嵌套数组,否则,添加
第二个数组的元素到对象映射。
- 最后,转换
将对象映射回合并值的数组。
代码:
const merge = (sourceArr, targetArr) => {
// (1) generate lookup map
const objMap = sourceArr.reduce((map, curr) => {
return {
...map,
[curr.name]: curr
};
}, {});
// (2) Merge objects, recurse on items arrays
targetArr.forEach((obj) => {
if (objMap[obj.name]) {
objMap[obj.name].items = merge(objMap[obj.name].items, obj.items);
} else {
objMap[obj.name] = obj;
}
});
// (3) Return merged values array
return Object.values(objMap);
};
const sourceObj = [
{
items: [
{ items: [{ id: "0", name: "z" }], name: "m" },
{ items: [{ id: "2", name: "q" }], name: "l" }
],
name: "c"
},
{
items: [{ items: [{ id: "4", name: "-" }], name: "v" }],
name: "d"
}
];
const targetObj = [
{
items: [
{ items: [{ id: "1", name: "d" }], name: "m" },
{ items: [{ id: "3", name: "b" }], name: "j" }
],
name: "c"
}
];
const merge = (sourceArr, targetArr) => {
const objMap = sourceArr.reduce((map, curr) => {
return {
...map,
[curr.name]: curr
};
}, {});
targetArr.forEach((obj) => {
if (objMap[obj.name]) {
objMap[obj.name].items = merge(objMap[obj.name].items, obj.items);
} else {
objMap[obj.name] = obj;
}
});
return Object.values(objMap);
};
const res = merge(sourceObj, targetObj);
console.log(res);
我发现一定程度的相互递归可以使代码更简洁。
const combineItems = (o = {}, items) =>
o .items || items .length ? {items: deepMerge (o.items || [], items)} : {}
const deepMerge = (xs, ys) =>
Object .values ([... xs, ... ys] .reduce (
(a, {name, items = [], ...rest}) => ({
... a,
[name]: {
... (a [name] || {name, ...rest}),
... combineItems (a [name], items)
}
}), {}
))
const sourceObj = [{items: [{items: [{id: "0", name: "z"}], name: "m"}, {items: [{id: "2", name: "q"}], name: "l"}], name: "c"}, {items: [{items: [{id: "4", name: "-"}], name: "v"}], name: "d"}]
const targetObj = [{items: [{items: [{id: "1", name: "d"}], name: "m"}, {items: [{id: "3", name: "b"}], name: "j"}], name: "c"}];
console .log (deepMerge (sourceObj, targetObj))
.as-console-wrapper {max-height: 100% !important; top: 0}
deepMerge
是主要功能,但它委托给 combineItems
来处理我们是否已经有要组合的项目的各种组合。 combineItems
将 return 类似于 {items: [<item1>. <item2>, ...]}
或只是 {}
,这取决于是否找到任何项目。
这里有一个潜在的性能问题,Rich Snapp 称之为 The reduce ({...spread}) anti-pattern。如果代码的性能令您满意,我个人不会在这里担心。但如果没有,我们可以将其更改为符合他的建议,如下所示:
const deepMerge = (xs, ys) =>
Object .values ([... xs, ... ys] .reduce (
(a, {name, items = [], ...rest}) => {
const x = a [name] || {name, ...rest}
if (items.length || x.items)
x .items = deepMerge (x .items || [], items)
a [name] = x
return a
}, {}
))
我有两个具有下一个结构的对象:
let sourceObj = [
{
items: [
{ items: [{ id: '0', name: 'z' }], name: 'm' },
{ items: [{ id: '2', name: 'q' }], name: 'l' },
],
name: 'c'
},
{
items: [
{ items: [{ id: '4', name: '-' }], name: 'v' },
],
name: 'd'
}
];
let targetObj = [
{
items: [
{ items: [{ id: '1', name: 'd' }], name: 'm' },
{ items: [{ id: '3', name: 'b' }], name: 'j' },
],
name: 'c'
}
];
我想合并这个对象以获得一个具有下一个结构的对象:
let merged = [
{
items: [
{ items: [
{ id: '0', name: 'd' },
{ id: '1', name: 'z' }],
name: 'm'
},
{ items: [{ id: '2', name: 'q' }], name: 'l' },
{ items: [{ id: '3', name: 'b' }], name: 'j' },
],
name: 'c'
},
{
items: [
{ items: [{ id: '4', name: '-' }], name: 'v' },
],
name: 'd'
}
]
也就是我要获取对象,如果源数组的名称与目标数组中的名称相同,则该对象已连接数组。
我尝试使用 lodash 方法 mergeWith,但我只能加入集合的上层项目...
mergeWith(sourceObj, targetObj, (objValue, srcValue) => {
if (isArray(objValue)) {
return objValue.concat(srcValue);
}
});
你说得对,Lodash 的 mergeWith
不会递归合并。您可以自己手动管理。
- 先遍历第一个数组,通过
name
属性. 生成一个map对象
- 然后遍历第二个数组并检查是否
name
属性 匹配,如果匹配则递归调用merge
函数进行合并 来自源数组和目标数组的两个嵌套数组,否则,添加 第二个数组的元素到对象映射。 - 最后,转换 将对象映射回合并值的数组。
代码:
const merge = (sourceArr, targetArr) => {
// (1) generate lookup map
const objMap = sourceArr.reduce((map, curr) => {
return {
...map,
[curr.name]: curr
};
}, {});
// (2) Merge objects, recurse on items arrays
targetArr.forEach((obj) => {
if (objMap[obj.name]) {
objMap[obj.name].items = merge(objMap[obj.name].items, obj.items);
} else {
objMap[obj.name] = obj;
}
});
// (3) Return merged values array
return Object.values(objMap);
};
const sourceObj = [
{
items: [
{ items: [{ id: "0", name: "z" }], name: "m" },
{ items: [{ id: "2", name: "q" }], name: "l" }
],
name: "c"
},
{
items: [{ items: [{ id: "4", name: "-" }], name: "v" }],
name: "d"
}
];
const targetObj = [
{
items: [
{ items: [{ id: "1", name: "d" }], name: "m" },
{ items: [{ id: "3", name: "b" }], name: "j" }
],
name: "c"
}
];
const merge = (sourceArr, targetArr) => {
const objMap = sourceArr.reduce((map, curr) => {
return {
...map,
[curr.name]: curr
};
}, {});
targetArr.forEach((obj) => {
if (objMap[obj.name]) {
objMap[obj.name].items = merge(objMap[obj.name].items, obj.items);
} else {
objMap[obj.name] = obj;
}
});
return Object.values(objMap);
};
const res = merge(sourceObj, targetObj);
console.log(res);
我发现一定程度的相互递归可以使代码更简洁。
const combineItems = (o = {}, items) =>
o .items || items .length ? {items: deepMerge (o.items || [], items)} : {}
const deepMerge = (xs, ys) =>
Object .values ([... xs, ... ys] .reduce (
(a, {name, items = [], ...rest}) => ({
... a,
[name]: {
... (a [name] || {name, ...rest}),
... combineItems (a [name], items)
}
}), {}
))
const sourceObj = [{items: [{items: [{id: "0", name: "z"}], name: "m"}, {items: [{id: "2", name: "q"}], name: "l"}], name: "c"}, {items: [{items: [{id: "4", name: "-"}], name: "v"}], name: "d"}]
const targetObj = [{items: [{items: [{id: "1", name: "d"}], name: "m"}, {items: [{id: "3", name: "b"}], name: "j"}], name: "c"}];
console .log (deepMerge (sourceObj, targetObj))
.as-console-wrapper {max-height: 100% !important; top: 0}
deepMerge
是主要功能,但它委托给 combineItems
来处理我们是否已经有要组合的项目的各种组合。 combineItems
将 return 类似于 {items: [<item1>. <item2>, ...]}
或只是 {}
,这取决于是否找到任何项目。
这里有一个潜在的性能问题,Rich Snapp 称之为 The reduce ({...spread}) anti-pattern。如果代码的性能令您满意,我个人不会在这里担心。但如果没有,我们可以将其更改为符合他的建议,如下所示:
const deepMerge = (xs, ys) =>
Object .values ([... xs, ... ys] .reduce (
(a, {name, items = [], ...rest}) => {
const x = a [name] || {name, ...rest}
if (items.length || x.items)
x .items = deepMerge (x .items || [], items)
a [name] = x
return a
}, {}
))