1

I am trying to merge my objects and get a result like below

{
    "sports": {
        "basketball": "kobe",
        "swimming": {
        },
        "football": "ronaldo",
        "running": "",
        "highJump": ""
    },
    "calendar": ["21", "25", "30"]
}
  • Somewhere I am doing wrong in my logic can you help me out
  • but if I alternate my sportA and sportsB value I am getting expected results...not sure what problem in my current scenario
  • providing my code below.
  • fiddle

https://jsfiddle.net/tjLk0frq/3/

var sportsA ={
    "sports": {
        "basketball": "kobe",
        "football": "ronaldo"
    }
};

var sportsB ={
    "sports": {
        "basketball": "",
        "swimming": {
        },
        "football": "",
        "running": "",
        "highJump": ""
    },
    "calendar": ["21", "25", "30"]
};

function merge(sportsA, sportsB) {
    for( var p in sportsB )
        if( sportsA.hasOwnProperty(p) )
            sportsA[p] = typeof sportsB[p] === 'object' ? merge(sportsA[p], sportsB[p]) : sportsB[p];

    return sportsA;
}

merge(sportsA, sportsB );
console.log("unexpected result" + sportsA ); 
console.log( sportsA ); 


//expected
/*
{
    "sports": {
        "basketball": "kobe",
        "swimming": {
        },
        "football": "ronaldo",
        "running": "",
        "highJump": ""
    },
    "calendar": ["21", "25", "30"]
}
*/
Derek 朕會功夫
  • 84,678
  • 41
  • 166
  • 228
  • should define what the merge rules are. Always take A values to over write B , or combine results of both A and B. – charlietfl Jan 27 '16 at 23:53

4 Answers4

1

You can use jQuery's extend method, with deep merge enabled, to do this:

var output = $.extend(true, sportsB, sportsA)

outputs:

{
    "sports": {
        "basketball": "kobe",
        "swimming": {},
        "football": "ronaldo",
        "running": "",
        "highJump": ""
    },
    "calendar": ["21", "25", "30"]
}
mark_c
  • 1,162
  • 1
  • 7
  • 10
  • @MarcosCasagrande: I don't get it. The OP wants empty strings to be overwritten by non-empty fields from the second object. I've compared mark_c answer with what OP proposed as a valid output and it seems to be working properly. – itachi Jan 27 '16 at 23:34
  • @itachi change the parameters ie (true, sportsA, sportsB) – NorCalKnockOut Jan 27 '16 at 23:35
  • @itachi I was getting a differente output in the console, don't know why. His answer works. +1 – Marcos Casagrande Jan 27 '16 at 23:38
  • really won't work universally...switch the order and results are different. Not sure about OP use case though either – charlietfl Jan 27 '16 at 23:49
  • @charlietfl - Can you expand on that comment a bit? I'm not sure what the issue would be. – mark_c Jan 28 '16 at 13:31
0

Here you go (pure JS):

function merge(obj1, obj2) {
    var result = {};

    for (var prop in obj1) {
        if (typeof obj1[prop] === "object" && typeof obj2[prop] === "object")
            result[prop] = merge(obj1[prop], obj2[prop]);
        else
            result[prop] = obj1[prop];
    }

    for (var prop in obj2) {
        result[prop] = (result[prop]? result[prop]: obj2[prop]);
    }

    return result;
}

console.log(merge(sportsA, sportsB));

This returns a new object, rather than modify an existing one, however.

In the first for..in loop, we check if we need to recurse first, otherwise set the property of result.

In the second for..in loop, we check if the property was already defined or if it's empty, and set the property accordingly.


Output:

{
    "sports": {
        "basketball": "kobe",
        "football": "ronaldo",
        "swimming": {},
        "running": "",
        "highJump": ""
    },
    "calendar": ["21", "25", "30"]
}

JSFiddle demo

Hatchet
  • 4,880
  • 1
  • 29
  • 42
  • thank you... can you tell me why swimming comes in third row instead of second row https://jsfiddle.net/tcgu3trx/ –  Jan 28 '16 at 00:43
  • @texirv The order doesn't matter. At all. https://stackoverflow.com/questions/5525795/does-javascript-guarantee-object-property-order – Hatchet Jan 28 '16 at 03:35
0

You have mistake when you check sportsA.hasOwnProperty(p) in your case you only update properties that are in sportsA, but not add new from sportsB.

Also if sportsB[p] has falsy value you don't want to update it for that I've used (sportsB[p] || sportsA[p]).

Check this code.

var sportsA ={
 "sports": {
     "basketball": "kobe",
     "football": "ronaldo"
 }
};

var sportsB ={
 "sports": {
     "basketball": "",
     "swimming": {
     },
     "football": "",
     "running": "",
     "highJump": ""
 },
 "calendar": ["21", "25", "30"]
};

function merge(sportsA, sportsB) {

    for( var p in sportsB )
        if( sportsA.hasOwnProperty(p) ) {
          sportsA[p] = typeof sportsB[p] === 'object' ? merge(sportsA[p], sportsB[p]) : (sportsB[p] || sportsA[p]);
        } else {
          sportsA[p] = sportsB[p];
        }
        

    return sportsA;
}

merge(sportsA, sportsB );
console.log("unexpected result" + sportsA ); 
console.log( sportsA ); 
Viktor Kukurba
  • 1,324
  • 8
  • 14
-2

The logic is breaking because when you only loop the property keys in one of the objects, you won't see the property keys that only exist in the other object.

You can get the root level keys of an object using Object.keys() which returns an array of the property names. Then you can merge the 2 sets of keys at same level and know all the final output properties needed

Then iterate those to get final results

charlietfl
  • 164,229
  • 13
  • 110
  • 143
  • what is supposed to happen when you have 2 string values, one in each? You left most of the strings empty and didn't cover that in expected results – charlietfl Jan 27 '16 at 23:40