-2

If I have two objects:

const obj1 = {
 "1,1" : "hello",
}

and

 const obj2 = {
 "1,1" : "hi",
}

what would be the easiest way to merge them into:

obj3 = {
"1,1": ["hello", "hi"],
} 

can i use the spread operator here? thanks!

Muhammad Usman
  • 10,039
  • 22
  • 39
guitard00d123
  • 347
  • 5
  • 14

3 Answers3

0

An alternative is using the function reduce.

const obj1 = {"1,1" : "hello"},
      obj2 = {"1,1" : "hi"},
      obj3 = [obj1, obj2].reduce((a, c) => {
                Object.keys(c).forEach(k => (a[k] || (a[k] = [])).push(c[k]));
                return a;
             }, {});

console.log(obj3);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Ele
  • 33,468
  • 7
  • 37
  • 75
  • Please don't post answers on obviously off topic / bad questions! [See: **Should one advise on off topic questions?**](//meta.stackoverflow.com/q/276572). This question is just a problem statement, expecting the answerer to do all the work for them. – Cerbrus May 16 '18 at 12:34
  • 1
    I don't think `reduce` should mutate any parameter, so `forEach` seems to be a better suit here. I'm not the downvoter though ;) – sp00m May 16 '18 at 12:34
  • @sp00m can you explain your statement? because I'm not mutating anything. – Ele May 16 '18 at 12:45
  • 1
    You're mutating the object `a` from the reduce rather than returning a new object - which is what @sp00m referred to. I'm sorry you feel like people aren't appreciating your work - but there is something really important in the people who spend a lot of time making sure we have a well thought out canonical knowledge base too - no need to hate on them. – Benjamin Gruenbaum May 16 '18 at 12:48
  • @BenjaminGruenbaum There is no mutation here. `a` is not one of the two input objects, it's a completely *new* object. See the last argument to reduce, `{}`. This code would be blowing up if he were trying to mutate either of the existing objects by `push`ing a string onto a string. – user229044 May 16 '18 at 12:52
  • People needs to understand the function `reduce` for sure. – Ele May 16 '18 at 12:53
  • Yes, `reduce` is semantically and technically the right choice here, not `forEach`. – user229044 May 16 '18 at 12:56
  • @meagar when people mean mutation here - they mean mutation of the same object between iterations - not that some external object is mutated. The reduce "isn't being honest" since the return value is being mutated as the collection is being folded. In certain functional languages for example this code is impossible. – Benjamin Gruenbaum May 16 '18 at 12:57
  • 1
    @BenjaminGruenbaum If you think that's what spOOm was referring to, how would `forEach` be any different? It would gradually accumulate all the properties by repeatedly mutating an object. – user229044 May 16 '18 at 12:58
  • Here is a non-mutating version @Ele: https://gist.github.com/benjamingr/127774037d2ce78497ff13c444382a62 . Also please don't call my technical competence into question when I'm trying to explain something you've asked to you. I'm sorry you had a negative experience - I'm on your side and trying to help we can do technical commenting all we want but let's stay friendly :)? – Benjamin Gruenbaum May 16 '18 at 12:59
  • @BenjaminGruenbaum a forEach will mutate an additional object to group the values. – Ele May 16 '18 at 12:59
  • @meagar When you see a `forEach` it's _obvious_ that it's mutating the object since its sole purpose is to cause a side effect - in the `reduce` it's unexpected behaviour. Again - I don't have strong feelings about this - this is why I've seen this pattern not pass code reviews before so I figured I'd chip in and try to clear the air. – Benjamin Gruenbaum May 16 '18 at 12:59
  • @BenjaminGruenbaum that gist is not an optimal approach because you're spreading every iteration which is slower and slower while the object is growing up. – Ele May 16 '18 at 13:00
  • I would suggest `Object.keys(c).forEach(k => {a[k]=(a[k]===undefined)?c[k]:[a[k],c[k]];return a;});` then if obj1 is `{"1,1" : "hello",a:false,o:"o"}` and obj2 is: `{"1,1" : "hi",a:true,b:"b"}` you'll get a better merge. – HMR May 16 '18 at 13:00
  • @BenjaminGruenbaum I'm really not calling your technical competence into question, and I don't know what negative experience you're referring to? I'm not trying to be unfriendly, my honest take on sp00ms comment is that they assume the values were being accumulated into the `obj1`, rather than a new object specifically created to collect the merged properties. – user229044 May 16 '18 at 13:03
  • @Ele I was not claiming it's a good approach - I was just claiming that's what a non-mutating reduce would look like which is what you asked sp00n to explain. I don't think it's that problematic to write less-than-beautiful code for a simple problem nor do I think it's a problem to spread at every iteration to be honest. I wouldn't worry about performance until it's a problem and between the two I'd prefer the non-mutating version - but I have no strong feelings about it and I was just trying to help clarify the comment. – Benjamin Gruenbaum May 16 '18 at 13:04
  • @meagar I was referring to Ele who wrote "People needs to understand the function reduce for sure.". I have never had a negative interaction experience with you before (and this isn't one either). I'm sorry for being unclear - comments make for a bad space for discussion and I got carried away commenting. If anyone is looking for me - I'm [in the chat](https://chat.stackoverflow.com/rooms/17/javascript) - feel free to ping me if you (or anyone else here) would like to discuss this issue further :) – Benjamin Gruenbaum May 16 '18 at 13:05
  • 1
    Let's stop the conversation. For sure, all of us want to help, so appreciate that. Hope these comments and my answer help in some way the requirement from the OP. – Ele May 16 '18 at 13:07
  • 2
    Wow, that escalated quickly haha! My point was just that you're mutating `a` here, before returning it (just as @BenjaminGruenbaum explained), which isn't semantically what `reduce` was made for in my opinion, in the functional world at least. Basically, your callback isn't _pure_, that's all. But anyway, this solution still looks good to me and does not deserve all these downvotes, I just wanted to share this side point :) – sp00m May 16 '18 at 13:18
  • 1
    @sp00m but `a` is an accumulator and it's created at the beginning on `reduce` call - there's no point in re-creating it in each iteration. MDN also doesn't do it in their [example](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce#Grouping_objects_by_a_property) – barbsan May 16 '18 at 13:28
0

a simple way to do this would be to use the internal for (.. in ...) of javascript : e.g.

obj1 = {1 : 2};
obj2 = {1 : 3};
obj3 = {};

for (element1 in obj1) {
 for (element2 in obj2) { 
   if (element1 == element2) {
        obj3[element1] = [];
        obj3[element1].push(obj1[element1]);
        obj3[element1].push(obj2[element2]);
    }
   }
  }
//result : obj3 = {1 : [2,3]}

but then what if obj1 = {"1,1" : 2, 1.1 : 3} or obj1 = {"1,1" : [1,2,3,4]} ? You really need to "well define" your requirement, but this should be a pretty good start.

LongChalk
  • 783
  • 8
  • 13
  • `{a: 1, a: 2}` makes no sense as an edge case. You can't assign different values to the same key in an object literal, the last value wins. Your hypothetical problem case `{"1,1" : 2, "1,1" : 3}` is identical to `{"1,1" : 3}`. – user229044 May 16 '18 at 12:54
  • yes, good point @meager . Try now with 1.1 vs "1.11" or something like that. I was trying to show that [1,2,3] is something different than [1,[2,3]]. This question needs refining... – LongChalk May 28 '18 at 10:06
0

var obj1 = {
    a: 1,
    b: 2
};

var obj2 = {
    a: 12,
    c: 20
};

console.log(merge(obj1, obj2));

function merge(obj1, obj2) {

    var obj3 = {};

    Object.keys(obj1).forEach(function(key) {
        pushIntoObj3(key, obj1[key]);
    });

    Object.keys(obj2).forEach(function(key) {
        pushIntoObj3(key, obj2[key]);
    });

    function pushIntoObj3(key, val) {
        if (!obj3[key]) {
            obj3[key] = [];
        }
        obj3[key].push(val);
    }

    return obj3;
}

OUTPUT :

{
  "a": [ 1, 12 ],
  "b": [ 2 ],
  "c": [ 20 ]
}
Cerbrus
  • 70,800
  • 18
  • 132
  • 147
Shivaji Varma
  • 690
  • 13
  • 24