Summing two objects without altering either

Posted on

Problem

In the slow progress of the project that generated my all-but-tumbleweed previous question I’ve needed to make sum-unions (any better term?) of simple objects where key:value is sizeClass:tally — I need the union of all sizeClasses, summing their values where they’re the same. I came up with this and I wonder whether anyone sees anything terrible about it or has a slicker way.

function mashObj(objA,objB) {
  var objC = {};
  var [ak,av] = deval(objA);
  var [bk,bv] = deval(objB);
  for (var i=0;i<ak.length;i++) {
    objC[ak[i]] = av[i];
  }
  for (i=0;i<bk.length;i++) {
    objC[bk[i]] = (objC[bk[i]] || 0) + bv[i];
  }
  return objC;
}
function deval(obj) {
  var ks = [];
  var vs = [];
  for (var k in obj) {
    ks.push(k);
    vs.push(Math.round(obj[k]));
  }
  return [ks, vs];
}

These functions are purpose-built, plenty of assumption about what’s coming in, so there’s been no attempt at generalizing. I go through deval() rather than doing a for..in because I also need to preserve the original two objects — I’ve been getting into a lot of trouble with the spooky-manipulation-at-a-distance problem.

By “sum-union” all I mean is that I might have something like:

plot1 = {2:4, 5:12, 7:3};
plot2 = {2:1, 4:3, 5:3};
  // and what I want to get is
total == {2:5, 4:3, 5:15, 7:3};

I assume that somewhere in math, logic or CS, probably all three, there’s a proper term for this.

Solution

Since you’re using ES2015 destructuring assignments you can use Set and spread:

function mashObj(a, b) {
    var sum = {};
    new Set([...Object.keys(a), ...Object.keys(b)])
        .forEach(k => sum[k] = Math.round(a[k]||0) + Math.round(b[k]||0));
    return sum;
}

Just a few improvement as the code is quite small.

You can shorten your deval() to this:

function deval(obj) {
  var arr = $.map(obj, function(value) {
    return value;
  });
  return [Object.keys(obj), arr];
}

plot1 = {2:4, 5:12, 7:3};
plot2 = {2:1,4:3,5:3};

console.log(deval(plot1));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

There is an Object.keys() that creates an array of all the keys of an object. Note, there is also an Object.values(), which is an experimental technology with little support in all browsers.
In place of that, I used a map() as shown in this example Get object values without looping.

Leave a Reply

Your email address will not be published. Required fields are marked *