4

Alright, so this is what I have in mind.

I have two example objects

let obj1 = { x: 60 };

let obj2 = { x: 9 };

I want to merge these objects in a way that their integer properties will combine as well, and not override each other, so the end result will be

let obj3 = { x: 69 };

So I've looked into Object.assign, but this function only merges properties in a way that it favors the properties on the first object if the other objects have a property with the same name, and doesn't sum integer properties.

I can, of course, just make a function that loops over each object's properties and creates a new object with the summed properties, but that'd be longer and I wanted to know if there's already a function that does it easily like the way Object.assign acts.

Thank you.

FightRay
  • 144
  • 14
  • 5
    There's no built-in facility to do that. – Pointy Feb 14 '19 at 14:18
  • 1
    This is not the definition of merging, what your looking at is more summing – CharybdeBE Feb 14 '19 at 14:20
  • @CharybdeBE I know that, that's what I eventually said in the question. What is the most efficient way to do it, then? Or is there no other way than just looping and looping. – FightRay Feb 14 '19 at 14:21
  • Yes, you won't get around looping (and notice that `Object.assign` isn't more efficient than looping, it's just syntactic sugar). Of course you can write some helper functions that will fit your need with the same ease of use. – Bergi Feb 14 '19 at 14:24

3 Answers3

4

If you want to use Lodash library you could do this with mergeWith method.

let obj1 = { x: 60, b: "foo", c: {y: 3, x: 'bar'} };
let obj2 = { x: 9, a: 'bar', c: {y: 4} };

const sum = _.mergeWith(obj1, obj2, (a, b) => {
  if (_.every([a, b], _.isNumber)) return a + b;
})

console.log(sum)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.js"></script>
Nenad Vracar
  • 118,580
  • 15
  • 151
  • 176
3

You could reduce the objects by taking an arbitrary count of objects and reduce the key/value pairs by respecting nested objects.

const
    add = (...a) => a                         // take all parameters
        .map(Object.entries)                  // get entries
        .reduce((a, b) => [...a, ...b], [])   // flat entries
        .reduce((o, [k, v]) => {
            o[k] = v && typeof v === 'object' // assign if object
                ? add(o[k] || {}, v)          //   result of recursive call
                : (o[k] || 0) + v;            //   or sum the value
            return o;
        }, {});

let obj1 = { x: 60, y: { z: 3 } },
    obj2 = { x: 9, y: { z: 1, a: 32 } },
    obj3 = { y: { a: 10 } },
    result = add(obj1, obj2, obj3);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
1

Like this?

let obj1 = { x: 60 };
let obj2 = { x: 9 };

let obj3 = [obj1, obj2].reduce((s,a)=>{return {x: s.x+a.x}}, {x:0});
 
console.log(obj3);

// or more shortener:

obj3 = {x:[obj1, obj2].reduce((s,a)=> s + a.x,0)};

console.log(obj3);

// for arbitrary properties, not just x:

keys = Object.keys(obj1),
obj3 = [obj1, obj2].reduce(function (r, o) {
    keys.forEach(function (k) {
        r[k] += o[k];
    });
    return r;
}, keys.reduce(function (r, k) {
    r[k] = 0;
    return r;
}, Object.create(null)));

console.log(obj3);
Artee
  • 824
  • 9
  • 19
  • I guess OP is looking to do this for arbitrary properties, not just `x`. – Bergi Feb 14 '19 at 14:33
  • This answers the question properly, but fails to go further like in Nenad Vracar's answer. This doesn't work on objects like `{foo: 10, bar: 'baz'}` or `{foo: {bar: 10}}` – Nino Filiu Feb 14 '19 at 14:34
  • Bergi, ok. I fix. – Artee Feb 14 '19 at 14:38
  • I haven't commented at first since your code was only for one value, and the code after the edit for arbitrary properties is not really an answer since I was looking for an easy, short way. Otherwise I can just make the function on my own... but thanks for the effort. – FightRay Feb 14 '19 at 14:43