1

With the following

    var a = {
        a: 0,
        b: [],
        c: "",
        d: { "x": "y", "g": "f" },
        e: {},
        f: function () { return undefined; }(),
        g: false
    };
    var b = {
        a: { "a": "b" },
        b: { "c": "d" },
        c: { "e": "f" },
        d: { "g": "h" },
        e: { "i": "j" },
        f: {},
        g: { "m": "n" },
    };

If you look at the datatypes, I have the falsy or empty version of several (or a close approximation): Number, Array,String,Object,undefined, Boolean.

Using the above with the following:

    var assinged = _.assign({}, a, b);
    var merged = _.merge({}, a, b);
    var extended = _.extend({}, a, b);

I get:

assigned:

{
    "a": {"a": "b"},
    "b": {"c": "d"},
    "c": {"e": "f"},
    "d": {"g": "h"},
    "e": {"i": "j"},
    "g": {"m": "n"}
}

merged:

{
    "a":{"a":"b"},
    "b":[],
    "c":{"e":"f"},
    "d":{"x":"y","g":"h"},
    "e":{"i":"j"},
    "g":{"m":"n"}
}

extended:

{
    "a": {"a": "b"},
    "b": {"c": "d"},
    "c": {"e": "f"},
    "d": {"g": "h"},
    "e": {"i": "j"},
    "g": {"m": "n"}
}

SO, assign and extend are equivalent for this example. Merge does the closest to what I want - You look at the key d, you can see that my existing key is preserved, and the object is extended. However, looking at the b key, you can see that the empty array is overwriting the object with Merge.

Two questions:

  1. Is there a simple command in lodash that will achieve what I want (a true, complete deep merge, where all properties in the first object are overwritten or extended with values from the second object)?

  2. What choice is _.merge making here, that is causing this to happen?

dgo
  • 3,877
  • 5
  • 34
  • 47

1 Answers1

4

However, looking at the b key, you can see that the empty array is overwriting the object with Merge

Not overwriting, no.

When you run _.merge({}, a, b).b you get an empty array with its c property set to "d". It's treating the array like an object, and effectively doing this:

var b = [];
b.c = "d";

// inspect what b is now
console.log(b.length); //=> 0
console.log(b.c); //=> "d"

In javascript an array is, effectively, an object that uses integers keys for many values:

var a = [1,2,3];
a.b = "c";
console.log(Object.keys(a)); //=> ["0", "1", "2", "b"]

You can create arbitrary properties on arrays and even functions in javascript, since they behave just like standard objects do.

So, yeah, it's merging the properties of object b into object a recursively exactly as it should.


You may want a more custom merging behavior that you write yourself. This question may help with that effort:

How to deep merge instead of shallow merge?

Alex Wayne
  • 178,991
  • 47
  • 309
  • 337
  • Wow! Thanks for that. In retrospect, this seems obvious, but I was definitely stumped. I think I'd just have to add some type of Array check to in there, because this is the only situation where this will arise, correct? Would this happen with functions since they can have custom properties in this fashion as well? – dgo Jul 20 '19 at 15:07
  • 1
    @dgo yep. It would definitely happen with functions, too – Alex Wayne Jul 20 '19 at 17:34