Håkan KA
Håkan KA

Reputation: 323

Merge two arrays of objects with override on key value

I am trying to merge data from json that comes in array of objects. I was using the underscore solution from here merge two json object based on key value in javascript, but it turns out it doesnt override existing items which I need to do as well now.

The result should be all items of array 1 in the same order, overriden by array 2 where id = id. Items in array 2 that does not exist in array 1 should be pushed to the end of the result.

First array:

[
 {id: 8, category: "A"}
 {id: 2, category: "D"}
 {id: 5, category: "C"}
 {id: 9, category: "B"}
]

Second array:

[
 {id: 1, category: "X"}
 {id: 2, category: "Y"}
]

Expected result:

[
 {id: 8, category: "A"}
 {id: 2, category: "Y"}
 {id: 5, category: "C"}
 {id: 9, category: "B"}
 {id: 1, category: "X"}
]

Upvotes: 3

Views: 2718

Answers (7)

Yuriy Rybin
Yuriy Rybin

Reputation: 25

This should work:

const newArr = second.reduce((res, item) => res.filter(i => i.id !== item.id).concat(item), first);

Upvotes: 0

Håkan KA
Håkan KA

Reputation: 323

Using underscore I managed to come up with this answer my own question. It is probably not the most efficent

const newarr = _.map(arr1, obj1 => {
  const r = _.find(arr2, obj2 => {
    return obj1[match] === obj2[match]
  })
  if (typeof r === 'undefined') {
    return obj1
  } else {
    return r
  }
})
_.each(arr2, obj => {
  if (_.indexOf(arr1, _.findWhere(arr1, {id: obj.id})) === -1) { 
   newarr.push(obj) 
  }
})

Upvotes: 0

mazza
mazza

Reputation: 59

I think reduce is better

first.reduce((res, item) => res.filter(i => i.id !== item.id).concat(item), second);

Upvotes: 0

Hassan Imam
Hassan Imam

Reputation: 22544

You can use a forEach to iterate through second array. For each object with the same id in the first array, update the category otherwise push in the new array.

const first = [{id: 8, category: "A"},{id: 2, category: "D"},{id: 5, category: "C"},{id: 9, category: "B"}],
      second = [{id: 12, category: "X"},{id: 2, category: "Y"}],
      merged = [...first]; 
second.forEach(o => {
  let obj = first.find(({id,category}) => id === o.id);
  obj ? obj.category = o.category : merged.push({...o});
});
console.log(merged);

Upvotes: 0

gurvinder372
gurvinder372

Reputation: 68393

Use filter, find and concat

Given that

var arr1 = [
 {id: 8, category: "A"},
 {id: 2, category: "D"},
 {id: 5, category: "C"},
 {id: 9, category: "B"}
];
var arr2 = [
 {id: 12, category: "X"},
 {id: 2, category: "Y"}
];

If the order is not important

var output = arr2.concat( 
        arr1.filter( s => 
            !arr2.find( t => t.id == s.id ) 
        )//end filter 
);//end concat

Demo

var arr1 = [{
    id: 8,
    category: "A"
  },
  {
    id: 2,
    category: "D"
  },
  {
    id: 5,
    category: "C"
  },
  {
    id: 9,
    category: "B"
  }
];
var arr2 = [{
    id: 12,
    category: "X"
  },
  {
    id: 2,
    category: "Y"
  }
];

var output = arr2.concat(
  arr1.filter(s =>
    !arr2.find(t => t.id == s.id)
  ) //end filter 
); //end concat

console.log(output);

If the order is important

var output = arr1.map( 
       s => arr2.find( 
           t => t.id == s.id ) || s 
).concat( //end map of arr1
      arr2.filter( 
           s => !arr1.find( t => t.id == s.id ) 
      ) //end filter
);//end concat

Demo

var arr1 = [{
    id: 8,
    category: "A"
  },
  {
    id: 2,
    category: "D"
  },
  {
    id: 5,
    category: "C"
  },
  {
    id: 9,
    category: "B"
  }
];
var arr2 = [{
    id: 12,
    category: "X"
  },
  {
    id: 2,
    category: "Y"
  }
];

var output = arr1.map(
  s => arr2.find(
    t => t.id == s.id) || s
).concat( //end map of arr1
  arr2.filter(
    s => !arr1.find(t => t.id == s.id)
  ) //end filter
); //end concat


console.log(output);

Upvotes: 9

Ankit Agarwal
Ankit Agarwal

Reputation: 30739

You can set a loop on your secondArray and check each object with id value against object with id of firstArray. If you find a match then simply replace the object else push the object:

var firstArray = [
 {id: 8, category: "A"},
 {id: 2, category: "D"},
 {id: 5, category: "C"},
 {id: 9, category: "B"}
];
var secondArray = [
 {id: 12, category: "X"},
 {id: 2, category: "Y"}
];

secondArray.forEach((obj)=>{
  var match = false;
  for(var i=0; i<firstArray.length; i++){
    if(firstArray[i].id === obj.id){
      match = true;
      firstArray[i] = obj;
      break;
    }
  }
  if(!match){
    firstArray.push(obj);
  }
});

console.log(firstArray);

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386654

You could use a Map as closure and store the index of the result array for this id.

var first = [{ id: 8, category: "A" }, { id: 2, category: "D" }, { id: 5, category: "C" }, { id: 9, category: "B" }],
    second = [{ id: 12, category: "X" }, { id: 2, category: "Y" }],
    result = [first, second].reduce((m => (r, a) => {
        a.forEach(o => {
            if (m.has(o.id)) {
                r[m.get(o.id)] = o;
                return;
            }
            m.set(o.id, r.push(o) - 1);
        });        
        return r;
    })(new Map), []);
    
console.log(result);

Upvotes: 0

Related Questions