How can I merge two arrays of objects where one object from one array is corresponding with one object in another array. Object which are not corresponding should be preserved. id and uid is corresponding condition.
const data1 = [{
id: 1,
someKey1: 'someValue2'
}, {
id: 2,
someKey2: 'someValue2'
}, {
id: 3,
someKey3: 'someValue3'
}]
const data2 = [{
uid: 1,
someKey4: 'someValue4'
}, {
uid: 2,
someKey5: 'someValue5'
}]
// expected result:
[{
someKey1: 'someValue2',
someKey4: 'someValue4'
}, {
someKey2: 'someValue2',
someKey5: 'someValue5',
{
id: 3,
someKey3: 'someValue3'
}
}]
6 Answers 6
You can grab all ids and uids from both sets of arrays and put them into a set, and then turn that set back into an array to get a list of unique ids/uids. Then, you can create maps for both arrays. Mapping the ids/uids to hold their corresponding object properties. Using the unique array of ids/uids, you can then .map() each id to its corresponding object held in the Maps like so:
const data1 = [{ id: 1, someKey1: 'someValue2'}, { id: 2, someKey2: 'someValue2' }, { id: 3, someKey3: 'someValue3' }]
const data2 = [{ uid: 1, someKey4: 'someValue4'}, { uid: 2, someKey5: 'someValue5' }];
const getMap = (arr, id) => new Map(arr.map(({[id]:_id, ...r}) => [_id, {...r}]));
const ids = [...new Set(data1.map(({id}) => id).concat(data2.map(({uid}) => uid)))];
const data1Map = getMap(data1, 'id');
const data2Map = getMap(data2, 'uid');
const result = ids.map(id => ({...(data1Map.get(id) || {}), ...(data2Map.get(id) || {})}));
console.log(result);
The main reason for creating the set is so that objects from data2 will be retained if data1 doesn't have that object's id and vice-versa.
Comments
You can create a function that iterates the array of data and put its object in a new array where the index is the id or uid
const data1 = [{ id: 1, someKey1: 'someValue2'}, { id: 2, someKey2: 'someValue2' }, { id: 3, someKey3: 'someValue3' }]
const data2 = [{ uid: 1, someKey4: 'someValue4'}, { uid: 2, someKey5: 'someValue5' }]
let result = [];
result = mergeIntoResult(result, data1, "id");
result = mergeIntoResult(result, data2, "uid");
console.log(JSON.stringify(result));
function mergeIntoResult(resultArray, dataArray, idProperty) {
return dataArray.reduce((acc,curr) => {
const index = curr[idProperty];
acc[index] = acc[index] || {}; //Take existing object or create default
acc[index] = {...acc[index], ...curr}; //Insert object data
delete acc[index][idProperty]; //Delete the "id" or "uid" field from the new object
return acc;
}, resultArray);
}
Comments
Here's another approach, which is not performance optimized and is less concise than the other answers, but breaks the steps down to make clear what's happening.
const data1 = [{ id: 1, someKey1: 'someValue2'}, { id: 2, someKey2: 'someValue2' }, { id: 3, someKey3: 'someValue3' }]
const data2 = [{ uid: 1, someKey4: 'someValue4'}, { uid: 2, someKey5: 'someValue5' }]
function mergeProperties(set1, linkingProperty1, set2, linkingProperty2){
const keys1 = set1.map( item => item[linkingProperty1] );
const keys2 = set2.map( item => item[linkingProperty2] );
const mergedKeys = keys1.concat(keys2);
const filteredKeys = mergedKeys.filter( key => !!key || key === 0); //truthy or actually number 0
const uniqueKeys = filteredKeys.filter((a, b) => filteredKeys.indexOf(a) === b);
// now we have a complete list, with no duplicates, of all possible ids
const mergedArray = uniqueKeys.reduce( (accumulator, key) => {
const resultInSet1 = set1.find( item => item[linkingProperty1] === key );
const resultInSet2 = set2.find( item => item[linkingProperty2] === key );
let item = {};
if(resultInSet1){
delete resultInSet1[linkingProperty1];
item = {...resultInSet1};
}
if(resultInSet2){
delete resultInSet2[linkingProperty2];
item = {...item, ...resultInSet2};
}
return [...accumulator, item];
}, []);
return mergedArray;
}
console.log( mergeProperties(data1,"id",data2,"uid") );
Comments
You can use map function. In addition, you can use Map collection to access items with O(1):
const createObj = (fooObj, keyToFilter) => {
if (fooObj)
return Object.fromEntries(Object.entries(fooObj).filter(([k, v])=> k != keyToFilter ));
return null;
}
const result = data1.map(s=> ({...createObj(s,'id'), ...createObj(maps.get(s.id),'uid')}));
An example:
const data1 = [{
id: 1,
someKey1: 'someValue2'
}, {
id: 2,
someKey2: 'someValue2'
}, {
id: 3,
someKey3: 'someValue3'
}];
const data2 = [{
uid: 1,
someKey4: 'someValue4'
}, {
uid: 2,
someKey5: 'someValue5'
}]
let maps = new Map(data2.map(s=> [s.uid, s]));
const createObj = (fooObj, keyToFilter) => {
if (fooObj)
return Object.fromEntries(Object.entries(fooObj).filter(([k, v])=> k != keyToFilter ));
return null;
}
const result = data1.map(s=> ({...createObj(s,'id'), ...createObj(maps.get(s.id),'uid')}));
console.log(result);
Comments
Here is a function that will do what you're looking for:
const objectMerge = (sourceData, joinData) =>
sourceData.map(item => {
const { id, ...restOfItem } = item
const foundJoins = joinData.filter(obj => obj.uid === id)
if (foundJoins.length === 0) {
return item
}
const dataFromJoins = foundJoins.reduce((result, join) => {
// Return everything in the object except the UID
const { uid, ...restOfFields } = join
return Object.assign({}, result, restOfFields)
}, {})
return Object.assign({}, restOfItem, dataFromJoins)
})
You can see it working here: https://repl.it/@RobBrander/Object-Merger
1 Comment
Try the below code:
let data1 = [{
id: 1,
someKey1: 'someValue2'
}, {
id: 2,
someKey2: 'someValue2'
}, {
id: 3,
someKey3: 'someValue3'
}]
let data2 = [{
uid: 1,
someKey4: 'someValue4'
}, {
uid: 2,
someKey5: 'someValue5'
}]
let result = [];
data1.forEach((obj, i) => {
const targetValues = data2.filter(x => x.uid === obj.id);
result[i] = obj;
if (targetValues && targetValues.length) {
delete obj.id;
targetValues.forEach(value => {
delete value.uid;
result[i] = {...result[i], ...value};
})
data2 = data2.filter(x => x.uid !== obj.id);
}
});
result = result.concat(data2);
console.log(result);
{id: 3in the expected result needs a key in the outer object.