合并 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 不会递归合并。您可以自己手动管理。

  1. 先遍历第一个数组,通过name属性.
  2. 生成一个map对象
  3. 然后遍历第二个数组并检查是否 name 属性 匹配,如果匹配则递归调用merge函数进行合并 来自源数组和目标数组的两个嵌套数组,否则,添加 第二个数组的元素到对象映射。
  4. 最后,转换 将对象映射回合并值的数组。

代码:

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
    }, {}
  ))