Pages

Lodash merge without overwriting

In many cases, you want to combine small objects into a big object. Lodash gives you _.merge() method to do so. (For more usages of _.merge(), see the lodash's docs).


let name = {name: 'Joe'},
    age = {age: 18},
    user = _.merge(name, age); // user = {name: 'Joe', age: 18}     

But what if you want to merge more than 2 objects together? Say, an array of objects? Well, remember apply method?


// arr is an array of objects that need to be merged

let result = _.merge.apply(null, arr);

That's easy! But keep in mind that, by default, if two keys are the same, _.merge will overwrite the values.


let userA = {name: 'Joe', age: 18},
    userB = {name: 'Anna', age: 17},
    users = _.merge(userA, userB);  // {name: 'Anna', age: 17}     

It's understandable because 1 object cannot have more than 1 key with the same name, so overwrite is needed. But what if you want to have your own rule for overwriting, e.g. keep the younger age or user name with lower alphabetical order. Or let say you want to throw an error if there is any conflict, i.e. there are 2 objects with the same key but different values. Lodash let you provide a customizer function to do just that.


_.merge(object, source, function(objectValue, sourceValue) {
    if (objectValue !== sourceValue) {
        throw new Error('Conflict values');
    }
    return objectValue;
});

The problem is how can you use customizer function and apply at the same time? The answer is _.partial or _.partialRight. These functions let you create new function that invokes a given function with partial arguments appended to it. See lodash docs.


mergeNoConflict = _.partialRight(_.merge, function(objVal, srcVal) {
    if (objVal && objVal !== srcVal) {
        throw new Error('Conflict values');
    }   
    return objectValue;
}); 

let result = mergeNoConflict.apply(null, arr);

From version 3.0 of lodash, the customizer function of _.merge is also provided with the key of object being merged as well as object and source. This can be really useful, for example, in previous example you may want to add the key as meta data to the error before throwing, etc.

5 comments:

  1. Use _.mergeWith(object, sources, customizer) since 4.0.0

    ReplyDelete