4

I'm working with javascript object literals, and got the brilliant idea that I'd streamline the actual object literal I had to work with, by abstracting all the common attributes away.

As such I'd like to extend/inherit/merge my concrete object from a baseline predefined object structure. What I mean is something like this:

Baseline:

{  
       "id":"",
       "options":[  
          {  
             "1":true,
              "concreteValue":"important"
          },
          {  
             "2":false,
              "concreteValue":"unimportant"
          },
          {  
             "3":true,
             "concreteValue":"important"
          }
       ],
       "greeting":"Howdy",
       "player":{  
          "name":"",
          "hp":1,
          "mana":10
       }
}

Concrete:

{  
   "id":"player1",
   "options":[  
      {  
         "5":true,
          "concreteValue":"awesome"
      }
   ],
   "greeting":"Greetings!",
   "player":{  
      "name":"player1",
      "hp":15
   }
}

and through extending/inheriting/merging concrete with baseline, I want the output to be this:

{  
   "id":"player1",
   "options":[  
      {  
         "1":true,
         "concreteValue":"important"
      },
      {  
         "2":false,
         "concreteValue":"unimportant"
      },
      {  
         "3":true,
         "concreteValue":"important"
      },
      {  
         "5":true,
         "concreteValue":"awesome"
      }
   ],
   "greeting":"Greetings!",
   "player":{  
      "name":"player1",
      "hp":15,
      "mana":10
   }
}

So values that exist both places are overwritten by those in concrete, values that does not already exist are added. This seem like a good way of doing things, so I'm sure there's a smart way of doing this, that I haven't found yet :) I looked at jQuery Extend, but it doesn't seem to merge values by the same name into each other.

I tried using concat and Object.Assign, but they didn't do the trick either. Anyone got any ideas?

Mike Brant
  • 70,514
  • 10
  • 99
  • 103
user3307017
  • 121
  • 2
  • 13
  • There is no built in solution in JavaScript for the behavior you want. To achieve the final merged object, you will need to iterate the keys in `concrete`. – rob Mar 27 '16 at 01:54
  • 1
    By the way, this has absolutely nothing to do with JSON, which is nothing more than a serialization format. You are talking about JavaScript object literals. – Mike Brant Mar 27 '16 at 02:06

2 Answers2

3

Lodash library can be used here.

I would probably choose @DelightedD0D solution not to add additional library if that's the only thing you will use Lodash for.

The advantage of using Lodash would be the customizer function where you can specify some custom merge goodies although i don't know if that is possible in jQuery extend approach.

You can use merge(src, dest, customizer) function to get what you described, here is the result:
http://jsbin.com/qibeji/edit?js,console

Lodash and Underscore are most optimal libraries for advanced javascript object manipulation.

Take a look at this two questions for a detailed overview of what you can do:
Lodash - difference between .extend() / .assign() and .merge()
Lodash: Constructing single object from many - Merging/overriding properties

Community
  • 1
  • 1
maljukan
  • 2,148
  • 2
  • 25
  • 35
  • 1
    This does not merge the objects correctly, see http://prnt.sc/akieg3. See how the values for 1 and 5 are in the same object? and there are only 3 objects in the options object where there should now be 4. This is equivalent to `jQuery.extend()` by itself I dont think you can do this automatically with any lib without changing the structure of the JSON. – Wesley Smith Mar 27 '16 at 01:43
  • True, need some modification – maljukan Mar 27 '16 at 01:47
  • 1
    Using an array inside the options object causes keys 0 of each object to be merged, they need to have named keys to prevent that – Wesley Smith Mar 27 '16 at 01:48
  • 1
    @DelightedD0D Updaited the answer, changed jsbin link – maljukan Mar 27 '16 at 02:04
  • @yup That'll do it :) – Wesley Smith Mar 27 '16 at 02:20
  • 1
    Now that it works... the only advantage of this approach would be the `customizer` function. – maljukan Mar 27 '16 at 02:23
  • I would've liked an unbiased function, but in my mind this is the most versatile solution to a generic problem - albeit it not a perfect one :) – user3307017 Mar 27 '16 at 08:26
2

To do this, you could change the strcture of the options objects like below, then jQuery.extend(true, baseline, concrete) would work

"options": {
    "5": {
      "someName": true,
      "concreteValue": "important"
    }
  },

See this jsFiddle which produces this result:

enter image description here

Wesley Smith
  • 19,401
  • 22
  • 85
  • 133
  • Creating nested objects instead of array entries does seem to work :) But I'd still like to be able to use arrays. Having to think of unique names is so tedious ;) – user3307017 Mar 27 '16 at 08:22