Lodash is trying to be helpful by recursively merging subobjects
This method is like _.assign except that it recursively merges own and inherited enumerable string keyed properties of source objects into the destination object. Source properties that resolve to undefined are skipped if a destination value exists. Array and plain object properties are merged recursively. Other objects and value types are overridden by assignment. Source objects are applied from left to right. Subsequent sources overwrite property assignments of previous sources.
https://lodash.com/docs/4.17.15#merge (emphasis mine)
If you don't want this to happen, just use the standard ES6 spread operator ... or Object.assign
const object1 = {
name: 'John',
age: 23,
books: ['book1', 'book2']
};
const object2 = {
name: 'John',
age: 23,
books: ['book3'],
city: 'London'
};
object3 = {...object1, ...object2};
object4 = Object.assign({}, object1, object2);
console.log(object3);
console.log(object4);
/* make the Stack Overflow console bigger */ .as-console-wrapper { max-height: 100vh !important; }
Run code snippetEdit code snippet Hide Results Copy to answer Expand
Answer from Samathingamajig on Stack OverflowIs there a benefit to using lodash.merge (or other utilities of the same feature) versus merging via object destructuring?
How to merge two arrays of objects by ID using lodash?
Lodash - How to merge two arrays of objects based on key?
How to use Lodash to merge two collections based on a key?
Is there some kind of edge case that lodash (or other merge utilities) takes care of that const output = {...a, ...b} doesn't? (Assuming a and b are both valid)
If both arrays are in the correct order; where each item corresponds to its associated member identifier then you can simply use.
var merge = _.merge(arr1, arr2);
Which is the short version of:
var merge = _.chain(arr1).zip(arr2).map(function(item) {
return _.merge.apply(null, item);
}).value();
Or, if the data in the arrays is not in any particular order, you can look up the associated item by the member value.
var merge = _.map(arr1, function(item) {
return _.merge(item, _.find(arr2, { 'member' : item.member }));
});
You can easily convert this to a mixin. See the example below:
_.mixin({
'mergeByKey' : function(arr1, arr2, key) {
var criteria = {};
criteria[key] = null;
return _.map(arr1, function(item) {
criteria[key] = item[key];
return _.merge(item, _.find(arr2, criteria));
});
}
});
var arr1 = [{
"member": 'ObjectId("57989cbe54cf5d2ce83ff9d6")',
"bank": 'ObjectId("575b052ca6f66a5732749ecc")',
"country": 'ObjectId("575b0523a6f66a5732749ecb")'
}, {
"member": 'ObjectId("57989cbe54cf5d2ce83ff9d8")',
"bank": 'ObjectId("575b052ca6f66a5732749ecc")',
"country": 'ObjectId("575b0523a6f66a5732749ecb")'
}];
var arr2 = [{
"member": 'ObjectId("57989cbe54cf5d2ce83ff9d8")',
"name": 'yyyyyyyyyy',
"age": 26
}, {
"member": 'ObjectId("57989cbe54cf5d2ce83ff9d6")',
"name": 'xxxxxx',
"age": 25
}];
var arr3 = _.mergeByKey(arr1, arr2, 'member');
document.body.innerHTML = JSON.stringify(arr3, null, 4);
body { font-family: monospace; white-space: pre; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.0/lodash.min.js"></script>
Create dictionaries for both arrays using _.keyBy(), merge the dictionaries, and convert the result to an array with _.values(). In this way, the order of the arrays doesn't matter. In addition, it can also handle arrays of different length.
const ObjectId = (id) => id; // mock of ObjectId
const arr1 = [{"member" : ObjectId("57989cbe54cf5d2ce83ff9d8"),"bank" : ObjectId("575b052ca6f66a5732749ecc"),"country" : ObjectId("575b0523a6f66a5732749ecb")},{"member" : ObjectId("57989cbe54cf5d2ce83ff9d6"),"bank" : ObjectId("575b052ca6f66a5732749ecc"),"country" : ObjectId("575b0523a6f66a5732749ecb")}];
const arr2 = [{"member" : ObjectId("57989cbe54cf5d2ce83ff9d6"),"name" : 'xxxxxx',"age" : 25},{"member" : ObjectId("57989cbe54cf5d2ce83ff9d8"),"name" : 'yyyyyyyyyy',"age" : 26}];
const merged = _(arr1) // start sequence
.keyBy('member') // create a dictionary of the 1st array
.merge(_.keyBy(arr2, 'member')) // create a dictionary of the 2nd array, and merge it to the 1st
.values() // turn the combined dictionary to array
.value(); // get the value (array) out of the sequence
console.log(merged);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.0/lodash.min.js"></script>
Using ES6 Map
Concat the arrays, and reduce the combined array to a Map. Use Object#assign to combine objects with the same member to a new object, and store in map. Convert the map to an array with Map#values and spread:
const ObjectId = (id) => id; // mock of ObjectId
const arr1 = [{"member" : ObjectId("57989cbe54cf5d2ce83ff9d8"),"bank" : ObjectId("575b052ca6f66a5732749ecc"),"country" : ObjectId("575b0523a6f66a5732749ecb")},{"member" : ObjectId("57989cbe54cf5d2ce83ff9d6"),"bank" : ObjectId("575b052ca6f66a5732749ecc"),"country" : ObjectId("575b0523a6f66a5732749ecb")}];
const arr2 = [{"member" : ObjectId("57989cbe54cf5d2ce83ff9d6"),"name" : 'xxxxxx',"age" : 25},{"member" : ObjectId("57989cbe54cf5d2ce83ff9d8"),"name" : 'yyyyyyyyyy',"age" : 26}];
const merged = [...arr1.concat(arr2).reduce((m, o) =>
m.set(o.member, Object.assign(m.get(o.member) || {}, o))
, new Map()).values()];
console.log(merged);
You can get the result using merge and keyBy lodash functions.
var array1 = [{id:1, name:'doc1'}, {id:2, name:'doc2'}];
var array2 = [{id:1, name:'doc1'}, {id:3, name:'doc3'}, {id:4, name:'doc4'}];
var merged = _.merge(_.keyBy(array1, 'id'), _.keyBy(array2, 'id'));
var values = _.values(merged);
console.log(values);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
Run code snippetEdit code snippet Hide Results Copy to answer Expand
Use _.unionBy() to combine two arrays, and remove duplicates that have the same unique field (id in this case). The lodash's union methods create an array of unique values. The _.unionBy() method accepts an iteratee which is invoked for each element of each array to generate the criterion by which uniqueness is computed.
const array1 = [{id:1, name:'doc1'}, {id:2, name:'doc2'}];
const array2 = [{id:1, name:'doc1'}, {id:3, name:'doc3'}, {id:4, name:'doc4'}];
const result = _.unionBy(array1, array2, 'id');
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
Run code snippetEdit code snippet Hide Results Copy to answer Expand
Second highest voted answer doesn't do proper merge. If second array contains an unique property, it is not taken into account.
This approach does a proper merge.
Lodash
var a = [
{ userId:"p1", item:1},
{ userId:"p2", item:2},
{ userId:"p3", item:4}
];
var b = [
{ userId:"p1", profile:1},
{ userId:"p2", profile:2},
{ userId:"p4", profile:4}
];
var merged = _.merge(_.keyBy(a, 'userId'), _.keyBy(b, 'userId'));
var values = _.values(merged);
console.log(values);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
Run code snippetEdit code snippet Hide Results Copy to answer Expand
ES6+
// from https://stackoverflow.com/a/34749873/80766
const mergeDeep = (target, ...sources) => {
if (!sources.length) return target;
const source = sources.shift();
if (target instanceof Object && source instanceof Object) {
for (const key in source) {
if (source[key] instanceof Object) {
if (!target[key]) Object.assign(target, { [key]: {} });
mergeDeep(target[key], source[key]);
} else {
Object.assign(target, { [key]: source[key] });
}
}
}
return mergeDeep(target, ...sources);
}
const a = [
{ userId:"p1", item:1},
{ userId:"p2", item:2},
{ userId:"p3", item:4}
];
const b = [
{ userId:"p1", profile:1},
{ userId:"p2", profile:2},
{ userId:"p4", profile:4}
];
const aKeyed = a.reduce((acc, cur) => ({ ...acc, [cur.userId]: cur }), {});
const bKeyed = b.reduce((acc, cur) => ({ ...acc, [cur.userId]: cur }), {});
const merged = mergeDeep(aKeyed, bKeyed);
const values = Object.values(merged);
console.log(values);
Run code snippetEdit code snippet Hide Results Copy to answer Expand
You can use _.map(), _.assign() and _.find().
// Iterate over first array of objects
_.map(a, function(obj) {
// add the properties from second array matching the userID
// to the object from first array and return the updated object
return _.assign(obj, _.find(b, {userId: obj.userId}));
});
Fiddle Demo
Show code snippet
var a = [{
userId: "p1",
item: 1
}, {
userId: "p2",
item: 2
}, {
userId: "p3",
item: 4
}];
var b = [{
userId: "p1",
profile: 1
}, {
userId: "p2",
profile: 2
}];
var arrResult = _.map(a, function(obj) {
return _.assign(obj, _.find(b, {
userId: obj.userId
}));
});
console.log(arrResult);
document.getElementById('result').innerHTML = JSON.stringify(arrResult, 0, 4);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.1.0/lodash.min.js"></script>
<pre id="result"></pre>
Run code snippetEdit code snippet Hide Results Copy to answer Expand