1

I want to merge two objects without cloning or mutating one of them.

function merge(defaultOptions, userOptions) {
    // userOptions should not be cloned or mutated but should get priority over defaultOptions
    return res;
}

I am using lodash but _merge does not help much. Since userOptions can be huge, I want to avoid cloning it.

Note that it is alright to mutate or clone defaultOptions as it would not affect performance of my system.

What is an optimal way to do this?

Govinda Totla
  • 576
  • 6
  • 19
  • do you want to get a new object or to mutate one? – Nina Scholz Sep 29 '20 at 14:43
  • Why is lodashes merge not of much help? It does exactly what you want. It mutates `defaultOptions`, which is ok for you. – Johannes Klauß Sep 29 '20 at 14:43
  • @NinaScholz I am open to mutating defaultOptions or creating a new one. As I mentioned I dont mind cloning defaultOptions so it doesn't matter anyway. – Govinda Totla Sep 29 '20 at 14:44
  • @JohannesKlauß _.merge(defaultOptions, userOptions) would clone the userOptions which is very costly. At the same time, _.merge(userOptions, defaultOptions) would override userOptions which I do not want. – Govinda Totla Sep 29 '20 at 14:45
  • @gt18 Where did you find that information? A quick look at the source code didn't indicate that the source objects are getting cloned. – Johannes Klauß Sep 29 '20 at 14:51
  • @JohannesKlauß, please check out http://jsfiddle.net/cv1fa8tr/. It should not be possible to not reflect changes after mutating the original object unless it was cloned. Also, I never went through the source code of _.merge, so I may be wrong. Can you let me know if I am missing something here? – Govinda Totla Sep 29 '20 at 15:52
  • That is not true. As @NinaScholz pointed out in her answer, you can just merge over the key and value. Then you also have the two objects merged, but there is no connection between them. And as far as I know, this is exactly what lodash is doing. – Johannes Klauß Sep 29 '20 at 16:20
  • @JohannesKlauß I understand your point, but my main concern is not how you do it but performance. Please check out http://jsfiddle.net/5bxd6ohv/1/. On my system, lodash takes about 3.5 seconds while the it is 0 on assigning. So lodash must be doing some heavy operation which I is costing me a lot. Note that this was just a simple example here and my goal is a little more complex. – Govinda Totla Sep 30 '20 at 06:57
  • How is it more complex? The example you gave just goes to the extremes regarding untyped array performance. This is not a lodash issue, but plain JavaScript. Do you have an array in your options object containing more than ten million untyped elements? Also did you try the merge with your real data? Is there any issue you encountered? This is a broad question and you didn't specifically tell us what exact problem you ran into with the suggested solution. – Johannes Klauß Sep 30 '20 at 07:52

3 Answers3

1

You could iterate the entries of userOptions and mutate defaultOptions.

function merge(defaultOptions, userOptions) {
    Object.entries(userOptions).forEach(([key, value]) => {
        if (value && typeof value === 'object') merge(defaultOptions[key] = defaultOptions[key] || [], value);
        else defaultOptions[key] = value;
    });
    return defaultOptions;
}
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
1

So, I implemented it myself and it seems to work just fine.

/**
 * Custom merge function with in place merge with behaviour similar to that of _.merge.
 *
 * @param {object} tar target object in which the other object is to be merged
 * @param {object} src source object which is to be merged
 * @returns object merged object
 */
function inPlaceMerge(tar, src) {
    let res = tar;
    if (_.isObject(src) && _.isObject(tar)) {
        _.forEach(src, (val, key) => {
                res[key] = inPlaceMerge(tar[key], val);
        })        
    } else if (src !== undefined) {
        res = src;
    }
    return res;
}
Govinda Totla
  • 576
  • 6
  • 19
-2

You can use Object destructuring this solution will make a newObject with the merge of the properties of both objects.

const newObject = {
  ...obj1,
  ...obj2
}

Assuming res is an object, you can try the following

return { ...res }
Leandro Matilla
  • 911
  • 4
  • 14