【问题标题】:In JavaScript there an elegant way to merge two Objects and sum any common properties?在 JavaScript 中有一种优雅的方式来合并两个对象并总结任何公共属性吗?
【发布时间】:2020-02-14 21:14:09
【问题描述】:

我正在对大量对象进行重复数据删除,其中许多对象具有一些共同的属性。所有属性都是整数。

循环遍历键并手动合并非常容易,但我不禁觉得Object.assignmapreduce 的组合可以做到这一点。随着语言的成熟,保持领先似乎是值得的。

编辑:例如:

let A = {
  "a": 10,
  "e": 2,
  "g": 14
}

let B = {
  "b": 3,
  "e": 15,
  "f": 1,
  "g": 2,
  "h": 11
}

let C = Object.magicMerge(A, B) 

/*
{
  "a": 10,
  "e": 17,
  "g": 16
  "b": 3,
  "f": 1,
  "h": 11
}
*/

【问题讨论】:

  • 不简单的部分是 sum any common properties
  • 我不确定您所说的“所有属性都是整数”是什么意思。对象中的所有键都是整数(这意味着它们实际上是字符串)还是值是整数?你能用一个示例输入/输出解释更多吗?
  • 添加了一个简单的例子——谢谢!
  • 你想要一个全新的对象还是只想要一个C === A(和Object.assign一样)?
  • 没关系 -- C === A 可以,但不是必需的

标签: javascript merge mapreduce duplicates


【解决方案1】:

在 vanilla JS 中实际上没有任何捷径,您必须显式地遍历每个对象和每个属性,并以某种方式总结它们。一种选择是与reduce 分组:

const arr = [{
  foo: 1,
  bar: 2
}, {
  bar: 5,
  baz: 7
}, {
  baz: 10,
  buzz: 10
}];

const combined = arr.reduce((a, obj) =>
  Object.entries(obj).reduce((a, [key, val]) => {
    a[key] = (a[key] || 0) + val;
    return a;
  }, a)
, {});

console.log(combined);

我更喜欢reduce,因为外部combined 变量一旦创建,就不会在其范围内发生变异,但如果您愿意,可以使用for..of

const arr = [{
  foo: 1,
  bar: 2
}, {
  bar: 5,
  baz: 7
}, {
  baz: 10,
  buzz: 10
}];

const combined = {};
for (const obj of arr) {
  for (const [key, val] of Object.entries(obj)) {
    combined[key] = (combined[key] || 0) + val;
  }
}

console.log(combined);

【讨论】:

  • 在一个循环中使用reduce,而在另一个循环中使用forEach 感觉很奇怪。
【解决方案2】:

这是没有循环/减少的东西:

let C = Object.fromEntries(
    Object.keys(A)
        .concat(Object.keys(B))
        .map(k => [k,
            (A[k] || 0) + (B[k] || 0)
        ])
)

这是一个适用于任意数量对象的通用函数:

let mergeSum = (...objs) => Object.fromEntries(
    Array.from(
        new Set(objs.flatMap(Object.keys)),
        k => [k,
            objs
                .map(o => o[k] || 0)
                .reduce((a, b) => a + b)
        ]))

C = mergeSum(A, B)

甚至更通用:

let mergeReduce = (objs, fn, init) => Object.fromEntries(
    Array.from(
        new Set(objs.flatMap(Object.keys)),
        k => [k, objs.map(o => o[k]).reduce(fn, init)]
    ));

// for example:

sumOfProps = mergeReduce([A, B],
    (a, b) => (a || 0) + (b || 0))

listOfProps = mergeReduce([A, B],
    (a, b) => b ? a.concat(b) : a,
    [])

【讨论】:

    【解决方案3】:

    我愿意:

    function objTotals(array){
      const r = {};
      array.forEach(o => {
        for(let i in o){
          if(!(i in r))r[i] = 0;
        }
      });
      for(let i in r){
        array.forEach(o => {
          if(i in o)r[i]+=o[i];
        });
      }
      return r;
    }
    const a = [{a:0, b:2, c:5, d:1, e:2}, {a:1, b:3}, {a:5, b:7, c:4}, {a:2, b:1, c:9, d:11}];
    console.log(objTotals(a));

    【讨论】:

      【解决方案4】:

      我会这样做:

      const a = { "a": 10, "e": 2, "g": 14 };
      const b = { "b": 3, "e": 15, "f": 1, "g": 2, "h": 11 };
      
      const sum = [...Object.entries(a), ...Object.entries(b)]
        .reduce(
          (acc, [key, val]) => ({ ...acc, [key]: (acc[key] || 0) + val }),
          {}
        );
      console.log("sum:", sum);
      你问的一个班轮:) 在我看来,这是最优雅的方式。

      【讨论】:

        【解决方案5】:

        是的,在我看来,您将需要合并键然后合并对象。尝试了一个更简单的解决方案,但是这个(虽然比我喜欢的要长)可以完成这项工作,并且适用于任意数量的对象。

        const object1 = {
          a: 4,
          c: 2.2,
          d: 43,
          g: -18 
        }, object2 = {
          b: -22.4,
          c: 14,
          d: -42,
          f: 13.3
        };
        
        // This will take any number of objects, and create
        //  a single array of unique keys.
        const mergeKeys = (objects)=>{
          let keysArr = objects.reduce((acc, object)=>{
            acc.push(...Object.keys(object));
            acc = [...new Set(acc.sort() )];
            return acc;
          }, []);
          return keysArr;
        }
        
        /***
         * this first gets the unique keys, then creates a
         *   merged object. Each object is checked for each
         *   property, and if it exists, we combine them.
         *   if not, we simply keep the current value.
         *
         ***/
        const combineAnyNumberOfObjects = (...objects)=>{
          const keys = mergeKeys(objects),
                returnObj = {};
          keys.forEach(key=>{
            returnObj[key]=0;
            objects.forEach(object=>{
              returnObj[key] = !!object[key] ? returnObj[key]+object[key] : returnObj[key]
            })
          })
        
          return returnObj;
        }
        
        console.log(combineAnyNumberOfObjects(object1, object2));

        正如您所说,合并和手动添加每个道具非常容易。上面的单行答案?美丽的。 ;)

        【讨论】:

        • mergeKeys 完全过于复杂且效率低下。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-11-16
        • 1970-01-01
        • 2020-07-05
        相关资源
        最近更新 更多