1

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'
 }
}]
Nick Parsons
51.7k6 gold badges62 silver badges80 bronze badges
asked Jan 28, 2020 at 13:06
2
  • 2
    Your expected result is not valid Javascript syntax. Commented Jan 28, 2020 at 13:12
  • To expand on @connexo comment, the object starting {id: 3 in the expected result needs a key in the outer object. Commented Jan 28, 2020 at 13:17

6 Answers 6

3

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.

answered Jan 28, 2020 at 13:17
Sign up to request clarification or add additional context in comments.

Comments

0

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);
}

answered Jan 28, 2020 at 13:19

Comments

0

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") );
answered Jan 28, 2020 at 13:27

Comments

0

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);

answered Jan 28, 2020 at 13:29

Comments

0

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

answered Jan 28, 2020 at 13:30

1 Comment

What if an item is in the second set of data, but not the first?
0

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);

answered Jan 28, 2020 at 13:22

3 Comments

What if an item si in data 2 but not in data 1?
Will be ignored. Does that need to be preserved?
That's what the OP said. "Object which are not corresponding should be preserved."

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.