Replacing objects in array

What's wrong with Object.assign(target, source) ?

enter image description here

Arrays are still type object in Javascript, so using assign should still reassign any matching keys parsed by the operator as long as matching keys are found, right?


There is always going to be a good debate on time vs space, however these days I've found using space is better for the long run.. Mathematics aside let look at a one practical approach to the problem using hashmaps, dictionaries, or associative array's whatever you feel like labeling the simple data structure..

    var marr2 = new Map(arr2.map(e => [e.id, e]));
    arr1.map(obj => marr2.has(obj.id) ? marr2.get(obj.id) : obj);

I like this approach because though you could argue with an array with low numbers you are wasting space because an inline approach like @Tushar approach performs indistinguishably close to this method. However I ran some tests and the graph shows how performant in ms both methods perform from n 0 - 1000. You can decide which method works best for you, for your situation but in my experience users don't care to much about small space but they do care about small speed.


Performance Measurement


Here is my performance test I ran for source of data

var n = 1000;
var graph = new Array();
for( var x = 0; x < n; x++){
  var arr1s = [...Array(x).keys()];
  var arr2s = arr1s.filter( e => Math.random() > .5);
  var arr1 = arr1s.map(e => {return {id: e, name: 'bill'}});
  var arr2 = arr2s.map(e => {return {id: e, name: 'larry'}});
  // Map 1
  performance.mark('p1s');
  var marr2 = new Map(arr2.map(e => [e.id, e]));
  arr1.map(obj => marr2.has(obj.id) ? marr2.get(obj.id) : obj);
  performance.mark('p1e');
  // Map 2
  performance.mark('p2s');
  arr1.map(obj => arr2.find(o => o.id === obj.id) || obj);
  performance.mark('p2e');
  graph.push({ x: x, r1: performance.measure('HashMap Method', 'p1s', 'p1e').duration, r2: performance.measure('Inner Find', 'p2s','p2e').duration});
}

You can use Array#map with Array#find.

arr1.map(obj => arr2.find(o => o.id === obj.id) || obj);

var arr1 = [{
    id: '124',
    name: 'qqq'
}, {
    id: '589',
    name: 'www'
}, {
    id: '45',
    name: 'eee'
}, {
    id: '567',
    name: 'rrr'
}];

var arr2 = [{
    id: '124',
    name: 'ttt'
}, {
    id: '45',
    name: 'yyy'
}];

var res = arr1.map(obj => arr2.find(o => o.id === obj.id) || obj);

console.log(res);

Here, arr2.find(o => o.id === obj.id) will return the element i.e. object from arr2 if the id is found in the arr2. If not, then the same element in arr1 i.e. obj is returned.


Since you're using Lodash you could use _.map and _.find to make sure major browsers are supported.

In the end I would go with something like:

function mergeById(arr) {
  return {
    with: function(arr2) {
      return _.map(arr, item => {
        return _.find(arr2, obj => obj.id === item.id) || item
      })
    }
  }
}

var result = mergeById([{id:'124',name:'qqq'}, 
           {id:'589',name:'www'}, 
           {id:'45',name:'eee'},
           {id:'567',name:'rrr'}])
    .with([{id:'124',name:'ttt'}, {id:'45',name:'yyy'}])

console.log(result);
<script src="https://raw.githubusercontent.com/lodash/lodash/4.13.1/dist/lodash.js"></script>