5

I want to remove same object from array by comparing 2 arrays.

Sample Data:

arr1 = [
  {id: 1, name: "a"},
  {id: 2, name: "b"},
  {id: 3, name: "c"},
  {id: 4, name: "d"},
];

arr2 = [
  {id: 1, name: "a"},
  {id: 4, name: "d"},
];

let newArray = []; // new array with with no same values it should be unique.
arr1.map((val, i)=>{
   arr2.map((val2)=>{
    if(val.id == val2.id){
       console.log('Matched At: '+ i) // do nothing
    }else{
      newArray.push(val);
    }
   })
})
console.log(newArray); // e.g: [{id: 2, name: "b"}, {id: 3, name: "c"},];
Najam Us Saqib
  • 3,190
  • 1
  • 24
  • 43
  • Does this answer your question? [Remove duplicate values from JS array](https://stackoverflow.com/questions/9229645/remove-duplicate-values-from-js-array) – Christos Lytras Mar 16 '20 at 14:59
  • 1
    what if arr2 contains some unique elements to be merged? for example `arr2 = [ {id: 1, name: "a"}, {id: 4, name: "d"}, {id: 5, name: "x"}, ]` – qiAlex Mar 16 '20 at 15:52

8 Answers8

7

Array.filter combined with not Array.some.

The trick here is also to not some,..

const arr1 = [
  {id: 1, name: "a"},
  {id: 2, name: "b"},
  {id: 3, name: "c"},
  {id: 4, name: "d"},
], arr2 = [
  {id: 1, name: "a"},
  {id: 4, name: "d"},
];

const newArray=arr1.filter(a=>!arr2.some(s=>s.id===a.id));

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

As mentioned in comments the question could be interpreted slightly differently. If you also want the unqiue items from arr2, you basically just do it twice and join. IOW: check what not in arr2 is in arr1, and then check what not in arr1 that's in arr2.

eg..

const notIn=(a,b)=>a.filter(f=>!b.some(s=>f.id===s.id));
const newArray=[...notIn(arr1, arr2), ...notIn(arr2, arr1)];

Update 2: Time complexity, as mentioned by qiAlex there is loops within loops. Although some will short circuit on finding a match, if the dataset gets large things could slow down. This is were Set and Map comes in.

So to fix this using a Set.

const notIn=(a,b)=>a.filter(a=>!b.has(a.id));
const newArray=[
  ...notIn(arr1, new Set(arr2.map(m=>m.id))),
  ...notIn(arr2, new Set(arr1.map(m=>m.id)))
];
Keith
  • 22,005
  • 2
  • 27
  • 44
  • what if `arr2` contains some unique elements to be merged? – qiAlex Mar 16 '20 at 15:25
  • 1
    @qiAlex You mean like a unique merge? would need to confirm with OP on that one, for me I interpreted the question as return one array that does not contain another. – Keith Mar 16 '20 at 15:42
  • At first I undesrtand a question as like as you did, it is a fair assumption, but wasn't clearly specified by OP. it is also possible to understand a question as 2 arrays that should be transformed into a one excluding duplicates, but theoretically both `arr1` and `arr2` can contain unique items. – qiAlex Mar 16 '20 at 15:55
  • 1
    @qiAlex You can basically do it the same way, just do it twice and join. I'll update answer with this info. – Keith Mar 16 '20 at 16:03
  • In my scenario I didn't have unique values in `arr2` so the answer worked perfectly. – Najam Us Saqib Mar 16 '20 at 16:53
2
const isInArray = (arr, id, name) => arr.reduce((result, curr) => ((curr.name === name && curr.id === id) || result), false)

const newArray = arr1.reduce((result, curr) => (isInArray(arr2, curr.id, curr.name) ? result : result.concat(curr)), [])
Rob Brander
  • 3,702
  • 1
  • 20
  • 33
1

You can update you code using filter() method, instead of using .map() method like:

const arr1 = [
  {id: 1, name: "a"},
  {id: 2, name: "b"},
  {id: 3, name: "c"},
  {id: 4, name: "d"},
], arr2 = [
  {id: 1, name: "a"},
  {id: 4, name: "d"},
];

let newArray = []; // new array with with no same values it should be unique.
newArray = arr1.filter(function(a) {
    for(var i=0; i < arr2.length; i++){
      if(a.id == arr2[i].id) return false;
    }
    return true;
});
console.log(newArray);
.as-console-wrapper { max-height: 100% !important; top: 0; }
palaѕн
  • 72,112
  • 17
  • 116
  • 136
1

You check each element in first array whether its id lies in the second array by using Array.prototype.some. If the element is not present then only yield it.

const arr1 = [
  {id: 1, name: "a"},
  {id: 2, name: "b"},
  {id: 3, name: "c"},
  {id: 4, name: "d"},
];

const arr2 = [
  {id: 1, name: "a"},
  {id: 4, name: "d"},
];

const result = arr1.filter(x => !arr2.some(y => y.id === x.id));

console.log(result);
Kunal Mukherjee
  • 5,775
  • 3
  • 25
  • 53
1

I think a simple comparer can works for getting differences and then concat them. with this method you dont need to check which array is bigger.

arr1 = [  {id: 1, name: "a"},  {id: 2, name: "b"},  {id: 3, name: "c"},  {id: 4, name: "d"}];

arr2 = [  {id: 1, name: "a"},  {id: 4, name: "d"},];

function localComparer(b){
  return function(a){
    return b.filter(
    function(item){
      return item.id == a.id && item.name == a.name
    }).length == 0;
  }
}

var onlyInArr1 = arr1.filter(localComparer(arr2));
var onlyInArr2 = arr2.filter(localComparer(arr1));

console.log(onlyInArr1.concat(onlyInArr2));
AbdurrahmanY
  • 809
  • 13
  • 22
1

We can filter values by checking whether some element is not contained in current array:

const result = arr1.reduce((a, c) => {
  if (!arr2.some(a2 => a2.id === c.id))
      a.push(c);
  return a;
}, [])

An example:

let arr1 = [
  {id: 1, name: "a"},
  {id: 2, name: "b"},
  {id: 3, name: "c"},
  {id: 4, name: "d"},
];

let arr2 = [
  {id: 1, name: "a"},
  {id: 4, name: "d"},
];

const result = arr1.reduce((a, c) => {
  if (!arr2.some(a2 => a2.id === c.id))
      a.push(c);
  return a;
}, [])

console.log(result);
StepUp
  • 36,391
  • 15
  • 88
  • 148
1

Try this one -

const arr1 = [
  {id: 1, name: "a"},
  {id: 2, name: "b"},
  {id: 3, name: "c"},
  {id: 4, name: "d"},
];

const arr2 = [
  {id: 1, name: "a"},
  {id: 4, name: "d"},
];

const arr3 = [...arr1, ...arr2];
const mySubArray = _.uniq(arr3, 'id');
console.log(mySubArray);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js"></script>
Kunal Mukherjee
  • 5,775
  • 3
  • 25
  • 53
Ankit Kumar Rajpoot
  • 5,188
  • 2
  • 38
  • 32
1

So many loops in every answer.

Complexity of the code my answer is 2N,

Idea is:

  1. to merge arrays.

  2. first loop - mark duplicates somehow

  3. second loop - filter duplicates out

arr1 = [
  {id: 1, name: "a"},
  {id: 2, name: "b"},
  {id: 3, name: "c"},
  {id: 4, name: "d"},
];

arr2 = [
  {id: 1, name: "a"},
  {id: 4, name: "d"},
];

let newArray = [...arr1, ...arr2].reduce((acc, item, index) => {
  acc.items.push(item);

  if (typeof acc.map[item.id] !== 'undefined') {
    acc.items[acc.map[item.id]] = null;
    acc.items[index] = null;
  }
  acc.map[item.id] = index;
  
  return acc
},  {map: {}, items: []}).items.filter(item => !!item)


console.log(newArray);
qiAlex
  • 4,290
  • 2
  • 19
  • 35
  • `Complexity of the code my answer is 2N,` Good point, if the dataset gets large, and performance is an issue doing this would be a good idea. Currently on this small dataset it's about 8X slower than a simple filter & some, but I don't think it would take much bigger dataset's before this gets faster. I'm up-voting this one for that reason. I suppose if performance is really paramount you could even do both by checking the lengths.. :) – Keith Mar 16 '20 at 16:41