0

I want to merge two array of objects where objects with the same ID will merge properties and objects with unique IDs will be its own object in the merged array. The following code does the first part where similar IDs will merge but how do I keep objects with unique ids from arr2 in the merged array and have it work with arrays of varying lengths?

Expected output:

[
  {
    "id": "1",
    "date": "2017-01-24",
    "name": "test"
  },
  {
    "id": "2",
    "date": "2017-01-22",
    "bar": "foo"
  }
  { "id": "3",
    "foo": "bar",
  }
]

The code:

let arr1 = [{
    id: '1',
    createdDate: '2017-01-24'
  },
  {
    id: '2',
    createdDate: '2017-01-22'
  },
];

let arr2 = [{
    id: '1',
    name: 'test'
  },
  {
    id: '3',
    foo: 'bar'
  },
  {
    id: '2',
    bar: 'foo'
  },
];

let merged = [];

for (let i = 0; i < arr1.length; i++) {
  merged.push({
      ...arr1[i],
      ...arr2.find((itmInner) => itmInner.id === arr1[i].id),
    },

  );
}

console.log(merged);

5 Answers 5

1

Iterate over the larger array, the one that contains the smaller array, instead:

let arr1=[{id:"1",createdDate:"2017-01-24"},{id:"2",createdDate:"2017-01-22"}],arr2=[{id:"1",name:"test"},{id:"3",foo:"bar"},{id:"2",bar:"foo"}];

const merged = arr2.map(item => ({
  ...arr1.find(({ id }) => id === item.id),
  ...item
}));
console.log(merged);

(if order matters, you can sort if afterwards too)

If you don't know in advance which one / if one will contain the other, then use an object to index the merged objects by IDs first:

let arr1=[{id:"1",createdDate:"2017-01-24"},{id:"2",createdDate:"2017-01-22"}],arr2=[{id:"1",name:"test"},{id:"3",foo:"bar"},{id:"2",bar:"foo"}];

const resultObj = Object.fromEntries(
  arr1.map(
    item => [item.id, { ...item }]
  )
);
for (const item of arr2) {
  if (!resultObj[item.id]) {
    resultObj[item.id] = item;
  } else {
    Object.assign(resultObj[item.id], item);
  }
}
const merged = Object.values(resultObj);
console.log(merged);

Sign up to request clarification or add additional context in comments.

2 Comments

So if I don't know which array is larger I will need to compare the length first?
Only if you're sure that one of the arrays contains all the IDs that the smaller array also contains. Otherwise, use the intermediate object technique instead.
0

You can create new object that contains the elems of arr1 and arr2 group by id key as follows and the merged array will be stored on object values.

You can get object values using Object.values func.

let arr1 = [{
    id: '1',
    createdDate: '2017-01-24'
  },
  {
    id: '2',
    createdDate: '2017-01-22'
  },
];

let arr2 = [{
    id: '1',
    name: 'test'
  },
  {
    id: '3',
    foo: 'bar'
  },
  {
    id: '2',
    bar: 'foo'
  },
];

const groupById = {};
for (let i = 0; i < Math.min(arr1.length, arr2.length); i ++) {
  if (arr1[i]) {
    groupById[arr1[i].id] = { ...groupById[arr1[i].id], ...arr1[i] };
  }
  if (arr2[i]) {
    groupById[arr2[i].id] = { ...groupById[arr2[i].id], ...arr2[i] };
  }
}

const merged = Object.values(groupById);
console.log(merged);

Comments

0

You could take a single loop approach by storing the objects in a hash table, sorted by id.

const
    mergeTo = (target, objects = {}) => o => {
        if (!objects[o.id]) target.push(objects[o.id] = {});
        Object.assign(objects[o.id], o);
    },
    array1 = [{ id: '1', createdDate: '2017-01-24' }, { id: '2', createdDate: '2017-01-22' }],
    array2 = [{ id: '1', name: 'test' }, { id: '3', foo: 'bar' }, { id: '2', bar: 'foo' }],
    merged = [],
    merge = mergeTo(merged);
    
array1.forEach(merge);
array2.forEach(merge);

console.log(merged);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Comments

0

A different approach could be merged the two array as is, and then "squash" it:

let arr1 = [{
    id: '1',
    createdDate: '2017-01-24'
  },
  {
    id: '2',
    createdDate: '2017-01-22'
  },
];

let arr2 = [{
    id: '1',
    name: 'test'
  },
  {
    id: '3',
    foo: 'bar'
  },
  {
    id: '2',
    bar: 'foo'
  },
];

let merged = [...arr1, ...arr2].reduce(
 (acc, {id, ...props}) => 
    (acc.set(id, {...(acc.get(id) || {}), ...props}), acc), new Map());

console.log([...merged].map( ([id, props]) => ({id, ...props}) ))

Notice that you might not need the last line, it used just to obtain the format you want to, since the above reduce is using a Map as accumulator, you can already access to everything with just merged.get("1").createdDate for example (where "1" is the id).

Since you're operating on one array by merging them at the beginning, you don't care about the length of them or even which one contains more elements. You can also have several arrays instead of just two, it doesn't matter.

What it matters is the order: if more than one array contains the same property for the same "id", the value you'll get is the value from the most recent array added (in the example above, would be arr2).

Comments

0

You can write a function to reduce the arrays to an object and then extract the value from that object which will return the values that you want. You can see the code below:

let arr1 = [
  {
    id: '1',
    createdDate: '2017-01-24',
  },
  {
    id: '2',
    createdDate: '2017-01-22',
  },
];

let arr2 = [
  {
    id: '1',
    name: 'test',
  },
  {
    id: '3',
    foo: 'bar',
  },
  {
    id: '2',
    bar: 'foo',
  },
];

function merge(arr1 = [], arr2 = []) {
  return Object.values(
    arr1.concat(arr2).reduce(
      (acc, curr) => ({
        ...acc,
        [curr.id]: { ...(acc[curr.id] ?? {}), ...curr },
      }),
      {}
    )
  );
}

const merged = merge(arr1, arr2);

Output:

[
  {
    "id": "1",
    "createdDate": "2017-01-24",
    "name": "test"
  },
  {
    "id": "2",
    "createdDate": "2017-01-22",
    "bar": "foo"
  },
  {
    "id": "3",
    "foo": "bar"
  }
]

Comments

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.