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'
  }
}]
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.

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

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

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

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

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

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

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.