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

33,956

Solution 1

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

Solution 2

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

Solution 3

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

Solution 4

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

Solution 5

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);
Share:
33,956
Sean O
Author by

Sean O

''Superb Parodist'' -- The New York Times. Director of IT. Web application developer. Digital craftsman &amp; curator.

Updated on December 25, 2021

Comments

  • Sean O
    Sean O over 2 years

    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?

    • spaceman
      spaceman about 8 years
      Is that what you are meaning? jsfiddle.net/4p98w3su not sure I understood the question.
    • Sean O
      Sean O about 8 years
      All good answers here; I like yours the best, using splice. If you want to convert this to an Answer, I'll mark as Accepted. Thanks.
    • spaceman
      spaceman about 8 years
      I put it as an answer, I think it is also faster than the methods involving map & reduce. I was getting about twice the speed when running it millions of times in a while loop.
  • Manish M Demblani
    Manish M Demblani about 8 years
    traversal is done in the reverse order since it is least time consuming, according to tests conducted by jspref
  • bharadhwaj
    bharadhwaj over 2 years
    This will only work for the flat lists. Won't work for this scenario.
  • Julian
    Julian over 2 years
    While Underscore is much smaller than Lodash, you can still get the same result in a single line using concat, groupBy, map and last. You can also add unionBy to Underscore in just five lines of code. See this answer.
  • Julian
    Julian over 2 years
    Single-line pure Underscore solution here: stackoverflow.com/a/70474201/1166087
  • Julian
    Julian over 2 years
    More compact and efficient solution using Underscore, which also adds new entries on update: stackoverflow.com/a/70474201/1166087
  • Julian
    Julian over 2 years
    While not asked in the original question, Backbone arguably has the best solution for updating data. See stackoverflow.com/a/70474027/1166087.
  • A. Rabus
    A. Rabus almost 2 years
    i need some explainer for this, please... :/
  • Nina Scholz
    Nina Scholz almost 2 years
    @A.Rabus, this code is acutally using a hash table for udate values and maps either the updates values or the original value. actually, i would separate the two steps into two parts and do not use this anymore.