_.assign only if the property exists in the target

I need to do something like _.assign, but only if the target already has the property assigned. Think about it, as source objects may have some properties to contribute, but also some properties that I don't want to mix.

I never used the _.assign callback mechanism, but tried the following. It "worked", but it still bound the property to the dest object (as undefined). I do not want him to be appointed at all.

_.assign(options, defaults, initial, function (destVal, sourceVal) { return typeof destVal == 'undefined' ? undefined : sourceVal; }); 

I wrote the following function for this, but I wonder if lodash already has something baked, more elegant.

 function softMerge (dest, source) { return Object.keys(dest).reduce(function (dest, key) { var sourceVal = source[key]; if (!_.isUndefined(sourceVal)) { dest[key] = sourceVal; } return dest; }, dest); } 
+11
javascript lodash
source share
3 answers

You can take only the keys to the first object

 var firstKeys = _.keys(options); 

Then we take the subset object from the second object, taking only those keys that exist on the first object:

 var newDefaults = _.pick(defaults, firstKeys); 

Then use this new object as an argument to _.assign :

 _.assign(options, newDefaults); 

Or in one line:

 _.assign(options, _.pick(defaults, _.keys(options))); 

It seemed like I was working when I tested it here: http://jsbin.com/yiyerosabi/1/edit?js,console

+20
source share

Here's an immutable deep version, I call it โ€œunion that preserves formโ€ in TypeScript that uses lodash:

 function _mergeKeepShapeArray(dest: Array<any>, source: Array<any>) { if (source.length != dest.length) { return dest; } let ret = []; dest.forEach((v, i) => { ret[i] = _mergeKeepShape(v, source[i]); }); return ret; } function _mergeKeepShapeObject(dest: Object, source: Object) { let ret = {}; Object.keys(dest).forEach((key) => { let sourceValue = source[key]; if (typeof sourceValue !== "undefined") { ret[key] = _mergeKeepShape(dest[key], sourceValue); } else { ret[key] = dest[key]; } }); return ret; } function _mergeKeepShape(dest, source) { // else if order matters here, because _.isObject is true for arrays also if (_.isArray(dest)) { if (!_.isArray(source)) { return dest; } return _mergeKeepShapeArray(dest, source); } else if (_.isObject(dest)) { if (!_.isObject(source)) { return dest; } return _mergeKeepShapeObject(dest, source); } else { return source; } } /** * Immutable merge that retains the shape of the 'existingValue' */ export const mergeKeepShape = <T>(existingValue: T, extendingValue): T => { return _mergeKeepShape(existingValue, extendingValue); } 

And a simple test to see how I see such a merge:

 let newObject = mergeKeepShape( { a : 5, // b is not here c : 33, d : { e : 5, // f is not here g : [1,1,1], h : [2,2,2], i : [4,4,4], } }, { a : 123, b : 444, // c is not here d : { e : 321, f : 432, // g is not here h : [3,3,3], i : [1,2], } } ); expect(newObject).toEqual({ a : 123, // b is not here c : 33, d : { e : 321, // f is not here, g : [1,1,1], h : [3,3,3], i : [4,4,4] } }); 

I myself used โ€œno changeโ€ in the test, but did not see the need to add it to this answer.

I hereby put this in the public domain.

0
source share

Another way to achieve this is to combine _.mapObject with _.has

 _.mapObject(object1, function(v, k) { return _.has(object2, k) ? object2[k] : v; }); 

Explanation:

  • Swipe all object1 key / value object1 with _.mapObject
  • Using _.has , check if the property name k also exists in object2 .
  • If so, copy the value assigned to the object2 k key back to object1 , otherwise just return the existing value of object1 ( v ).

Plunkr

0
source share

All Articles