0

I'm using underscore, and I'm wondering if there's a more efficient way of accomplishing the following - seems like this should be a function in underscore.

var obj1 = { test: 'hello' },
    obj2 = { test: 'hello2' };

var merged = {};
_.each(_.keys(obj1), function(key) { merged[key] = [obj1[key], obj2[key]]; });

console.log(merged);

Also the limitations of this are that any keys in obj2 not present in obj1 will not be counted - it would be nice if I could get something akin to a full outer join, where the value of one side or the other will be null if it doesn't exist. Also, it would be nice if this wasn't limited to two objects.

4 Answers 4

2

You can do this with plain JS, and multiple objects:

var merge = function() {
  return [].reduce.call(arguments, function(acc, obj) {
    Object.keys(obj).forEach(function(k) {
      acc[k] = (acc[k] || []).concat(obj[k])
    })
    return acc
  }, {})
}

var a = {
  prop: 1
}
var b = {
  prop: 2
}
var c = {
  prop: 3
}

console.log(merge(a, b, c)) //=> {prop: [1,2,3]}

Sign up to request clarification or add additional context in comments.

5 Comments

This is clean, but doesn't quite hit the nice-to-haves which is ensuring null values are included for objects that don't contain certain values. I'm not sure if it's intuitive to do that or not, but that's what I'm after.
Not sure I understand what you're saying, do you have an example of input and output?
Well, given you have the input objects o1 and o2, they're being sent into the method in a specific order. So in my mind, you would expect the output data to be at the same index in the new format. For example: jsfiddle.net/rs4zmb6o
Why would you need to do that? Having undefineds in your code doesn't seem like a good idea. You may be facing an XY problem...
Perhaps, which is why I haven't picked any answer yet. I'm trying to determine what is truly the correct answer for what I've asked, not just my use case. The case I'm thinking about is one where you'd want to know the position of that value with respect to the original list of objects. Perhaps you're creating a table of values, and you want to fill a column with data. But is this use case more likely than the merge you've described? I don't know.
1

There may be a more underscore-ish way to do it, but it's a fairly straightforward function in plain JS:

function mergeObjects() {
  var merged = {};

  for ( var i = 0; i < arguments.length; ++i )
    {
      var o = arguments[i];

      for ( var f in o )
        if (o.hasOwnProperty(f))
          {
            merged[f] = merged[f] || [];
            merged[f].push(o[f]);
          }
    }

  return merged;
}

This version accepts any number of arguments, assuming they're all objects.

Example: http://codepen.io/paulroub/pen/sDcym

1 Comment

Another good example, but again doesn't do the whole "if it's null, throw it in there anyway to maintain a consistent array size" thing - which I understand is generally not required but may be useful to those of us looking to maintain the order of the data between input and output.
1

I'll throw my hat in the ring here as well:

function merge(){
  var objects = arguments,
      merged = {},
      keys = _.union(_.flatten(_.map(objects, function(arg){ return _.keys(arg); })));
  _.each(keys, function(key){
      _.each(objects, function(obj){
          merged[key] = merged[key] || [];
          merged[key].push(obj.hasOwnProperty(key) ? obj[key] : null);
      });
  });
  return merged;
}

Can't speak for it's readability or speed, as I don't think it wins in either category. However, besides your own answer I think this is the only one that actually meets all of the requirements.

example: http://jsfiddle.net/z01ubsny/1/

2 Comments

Yes, this totally works - though I don't think it's quite as discrete as mine! :)
Yeah you're right. Just wanted an underscore excersise and at the time all other answers did not meet the criteria. Updated mine to return null for unset values, by the way.
0

Here's a better solution than what I provided in my original post, though I'm still not convinced it's optimal.

 var objs = [{
   test: 'hello',
   test2: 'word',
   test3: 2
 }, {
   test: 'hello2',
   test0: 1
 }];

 var merged = _.chain(_.defaults.apply(_, objs))
   .keys()
   .map(function(k) {
     return [k, _.pluck(objs, k)]
   })
   .object()
   .value();

 console.log(merged);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

2 Comments

I wanted to compare speed between your answer and mine, but I can only get this one to return an object of arrays, with each array only containing undefined. Am I missing something? jsfiddle.net/z01ubsny/3
Improper use of arguments - just assign arguments to a variable and it'll work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.