_.assign only if property exists in target object
You could take just the keys from the first object
var firstKeys = _.keys(options);
Then take a subset object from the second object, taking only those keys which exist on the first object :
var newDefaults = _.pick(defaults, firstKeys);
Then use that new object as your argument to _.assign
:
_.assign(options, newDefaults);
Or in one line :
_.assign(options, _.pick(defaults, _.keys(options)));
Seemed to work when I tested it here : http://jsbin.com/yiyerosabi/1/edit?js,console
Here is a immutable deep version, I call it "merge that retains the shape", 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 vision such merge should work:
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 used seamless-immutable myself in the test, but didn't see a need to put it in this answer.
I hereby place this in the Public Domain.
Another way to accomplish this is by combining _.mapObject
with _.has
_.mapObject(object1, function(v, k) {
return _.has(object2, k) ? object2[k] : v;
});
Explanation:
- Traverse all key/value pairs of
object1
using_.mapObject
- Using
_.has
, check if property namek
also exists inobject2
. - If it does, copy the value assigned to key
object2
'sk
back toobject1
, else, just return the existing value of object1 (v
).
Plunkr