Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript - merge two arrays of objects and de-duplicate based on property value

I want to update (replace) the objects in my array with the objects in another array. Each object has the same structure. e.g.

var origArr = [
  {name: 'Trump', isRunning: true},
  {name: 'Cruz', isRunning: true},
  {name: 'Kasich', isRunning: true}
];
var updatingArr = [
  {name: 'Cruz', isRunning: false},
  {name: 'Kasich', isRunning: false}
];
// desired result:
NEWArr = [
  {name: 'Trump', isRunning: true},
  {name: 'Cruz', isRunning: false},
  {name: 'Kasich', isRunning: false}
];

I've tried concat() & Underscore's _.uniq function, but it always dumps the newer object & returns, essentially, the original array.

Is there a way to overwrite (replace) origArr with the objects in updatingArr -- matching on the name property?

like image 552
Sean O Avatar asked May 05 '16 18:05

Sean O


People also ask

How can I merge two arrays in JavaScript?

The concat() method concatenates (joins) two or more arrays. The concat() method returns a new array, containing the joined arrays. The concat() method does not change the existing arrays.

How do you merge duplicate objects in array?

To merge duplicate objects in array of objects with JavaScript, we can use the array map method. to merge the items with the label value together. To do this, we get an array of labels without the duplicates with [... new Set(data.

How do you combine properties of two objects?

To merge objects into a new one that has all properties of the merged objects, you have two options: Use a spread operator ( ... ) Use the Object. assign() method.


11 Answers

I came here looking for exactly this, saw @Gruff Bunny 's technique and wondered if 'lodash' wouldn't perhaps be a superior option even to 'underscore'?

Lo and behold :

let result = _.unionBy(updatingArr, origArr, 'name');
like image 188
Martin Bramwell Avatar answered Oct 03 '22 09:10

Martin Bramwell


Using a double for loop and splice you can do it like so:

for(var i = 0, l = origArr.length; i < l; i++) {
    for(var j = 0, ll = updatingArr.length; j < ll; j++) {
        if(origArr[i].name === updatingArr[j].name) {
            origArr.splice(i, 1, updatingArr[j]);
            break;
        }
    }
}

Example here

like image 27
spaceman Avatar answered Oct 03 '22 08:10

spaceman


You could use Array#map in combination with Array#reduce

var origArr = [{ name: 'Trump', isRunning: true }, { name: 'Cruz', isRunning: true }, { name: 'Kasich', isRunning: true }],
    updatingArr = [{ name: 'Cruz', isRunning: false }, { name: 'Kasich', isRunning: false }],
    NEWArr = origArr.map(function (a) {
        return this[a.name] || a;
    }, updatingArr.reduce(function (r, a) {
        r[a.name] = a;
        return r;
    }, Object.create(null)));

document.write('<pre>' + JSON.stringify(NEWArr, 0, 4) + '</pre>');

UPDATE 2022

Using an object with name as hash and mapping the original array by taking the update from hash table or the original object.

const
    origArr = [{ name: 'Trump', isRunning: true }, { name: 'Cruz', isRunning: true }, { name: 'Kasich', isRunning: true }],
    updatingArr = [{ name: 'Cruz', isRunning: false }, { name: 'Kasich', isRunning: false }],
    updates = Object.fromEntries(updatingArr.map(o => [o.name, o])),
    result = origArr.map(o => updates[o.name] || o);

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

Another approach by using Map.

This approach works for objects who are only in the updating array as well.

const
    origArr = [{ name: 'Trump', isRunning: true }, { name: 'Cruz', isRunning: true }, { name: 'Kasich', isRunning: true }],
    updatingArr = [{ name: 'Cruz', isRunning: false }, { name: 'Kasich', isRunning: false }],
    result = Array.from([...origArr, ...updatingArr]
        .reduce((m, o) => m.set(o.name, o), new Map)
        .values()
    );

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
like image 43
Nina Scholz Avatar answered Oct 03 '22 09:10

Nina Scholz


const origArr = [
  {name: 'Trump', isRunning: true},
  {name: 'Cruz', isRunning: true},
  {name: 'Kasich', isRunning: true}
];

const updatingArr = [
  {name: 'Cruz', isRunning: false},
  {name: 'Kasich', isRunning: false}
];

let hash = {};

for(let i of origArr.concat(updatingArr)) {
  if(!hash[i]) {
    hash[i.name] = i;
  }
}

let newArr = [];

for(let i in hash) {
  newArr.push(hash[i])
}

console.log(newArr);
like image 29
Sourabh Khurana Avatar answered Oct 03 '22 08:10

Sourabh Khurana


You can use a hash which gives the index by name, and Object.assign to update.

var hash = origArr.reduce(function(hash, obj, index) {
  hash[obj.name] = index;
  return hash;
}, Object.create(null));
for(var obj of updatingArr) {
  Object.assign(origArr[hash[obj.name]], obj);
}
like image 33
Oriol Avatar answered Oct 03 '22 08:10

Oriol


You can give this a try.

var origArr = [
  {name: 'Trump', isRunning: true},
  {name: 'Cruz', isRunning: true},
  {name: 'Kasich', isRunning: true}
];
var updatingArr = [
  {name: 'Cruz', isRunning: false},
  {name: 'Kasich', isRunning: false}
];

var origLength = origArr.length;
var updatingLength = updatingArr.length;

//Traverse the original array and replace only if the second array also has the same value
for(i = origLength-1; i >= 0; i--) {
    for(j = updatingLength -1; j >= 0; j--) {
    if(origArr[i].name === updatingArr[j].name) {
        origArr[i] = updatingArr[j];
    }
  }
}

console.log(origArr);
like image 24
Manish M Demblani Avatar answered Oct 03 '22 10:10

Manish M Demblani


Here's a solution using underscore:

var result = _.map(origArr, function(orig){
    return _.extend(orig, _.findWhere(updatingArr, {name: orig.name}));
});
like image 34
Gruff Bunny Avatar answered Oct 03 '22 10:10

Gruff Bunny


This version lets you define the selector that defines an object as duplicate.

  • forEach iterates over the new data
  • findIndex returns an index >= 0 if two selectors are equal. If none are equal, it returns -1
  • If there is a duplicate, we use slice to replace the original by the new.
  • If there's no duplicate, we push it into the original array.

const origArr = [
  {name: 'Trump', isRunning: true},
  {name: 'Cruz', isRunning: true},
  {name: 'Kasich', isRunning: true}
];

const updatingArr = [
  {name: 'Cruz', isRunning: false},
  {name: 'Kasich', isRunning: false}
];

const mergeArrayOfObjects = (original, newdata, selector = 'key') => {
	newdata.forEach(dat => {
		const foundIndex = original.findIndex(ori => ori[selector] == dat[selector]);
		if (foundIndex >= 0) original.splice(foundIndex, 1, dat);
        else original.push(dat);
	});

	return original;
};

const result = mergeArrayOfObjects(origArr, updatingArr, "name")
console.log('RESULT -->', result)
like image 28
aquiseb Avatar answered Oct 03 '22 09:10

aquiseb


This will do what you need:

var origArr = [
  {name: 'Trump', isRunning: true},
  {name: 'Cruz', isRunning: true},
  {name: 'Kasich', isRunning: true}
];

var updatingArr = [
  {name: 'Cruz', isRunning: false},
  {name: 'Kasich', isRunning: false}
];

for (var i = 0; i < updatingArr.length; ++i) {
  var updateItem = updatingArr[i];
  for (var j = 0; j < origArr.length; ++j) {
    var origItem = origArr[j];
    if (origItem.name == updateItem.name) {
      origItem.isRunning = updateItem.isRunning;
      break;    
    }
  }
}

document.write('<pre>' + JSON.stringify(origArr, 0, 4) + '</pre>');
like image 34
gevorg Avatar answered Oct 03 '22 08:10

gevorg


Same as @gevorg answer, but you may want to also add a new object to the original array if no matches are found.

let combinedEvents = origEvents;
for(let i =0; i< newEvents.length; i++){
  let newEvent = newEvents[i];
  for(let j =0; j< origEvents.length; j++){
    let origEvent = origEvents[j];
    if(newEvent.events_id == origEvent.events_id){
      combinedEvents.splice(j,1, newEvent);
      break;
    } else if(j === origEvents.length - 1){
      combinedEvents.push(newEvent);
      break;
    }
  }
}
like image 30
Vincent Tang Avatar answered Oct 03 '22 08:10

Vincent Tang


In ES6 you can use the object Map like this...

let map = new Map();
let origArr = [
  {name: 'Trump', isRunning: true},
  {name: 'Cruz', isRunning: true},
  {name: 'Kasich', isRunning: true}
];
let updatingArr = [
  {name: 'Cruz', isRunning: false},
  {name: 'Kasich', isRunning: false}
];

// Concating arrays with duplicates
let NEWArr = origArr.concat(updatingArr);

// Removing duplicates items
NEWArr.forEach(item => {
  if(!map.has(item.name)){
    map.set(item.name, item);
  }
});

Array.from(map.values());

Remember: the Map object need in unique key, in this case I used the name.

like image 42
mariosantos Avatar answered Oct 03 '22 09:10

mariosantos