How to subtract an array

Asked

Viewed 1,028 times

7

Assuming I have an array

let arr1 = [
     {nome: 'Fulano', idade: 19}, 
     {nome: 'Ciclano',idade: 20}, 
     {nome: 'João',   idade: 20}, 
     {nome: 'Maria',  idade: 30}, 
     {nome: 'Teste',  idade: 52}
]

and I have another

let arr2 = [
         {nome: 'Fulano', idade: 19}, 
         {nome: 'Ciclano',idade: 20}, 
]

How do I remove from arr1 objects which are equal to arr2. For example, thus returning a new array:

let arr3 = [
             {nome: 'João',   idade: 20}, 
             {nome: 'Maria',  idade: 30}, 
             {nome: 'Teste',  idade: 52} 
    ]
  • 2

    It would be a case of subtraction and not left Join.

  • Exact: subtract one array from another

  • I changed the title, see if it’s clearer please.

6 answers

9


Create an object arr3 equal to arr1 and loop the arr2 checking whether it exists in the arr1. If available, remove from arr3 with splice. At the end you’ll have a arr3 without the items in the arr2:

let arr1 = [
     {nome: 'Fulano', idade: 19}, 
     {nome: 'Ciclano',idade: 20}, 
     {nome: 'João',   idade: 20}, 
     {nome: 'Maria',  idade: 30}, 
     {nome: 'Teste',  idade: 52}
]

let arr2 = [
   {nome: 'Fulano', idade: 19}, 
   {nome: 'Ciclano',idade: 20}
]

let arr3 = arr1;

for(let it2 in arr2){
   for(let it1 in arr1){
      if(JSON.stringify(arr2[it2]) == JSON.stringify(arr1[it1])){
         arr3.splice(it1, 1);
         break; // se achou, não precisa continuar o loop
      }
   }
}

console.log(arr3);

Edit

Converted items into string for comparison as a whole, not by values.

Edit 2

Another way using filter:

let arr1 = [
     {nome: 'Fulano', idade: 19}, 
     {nome: 'Ciclano',idade: 20}, 
     {nome: 'João',   idade: 20}, 
     {nome: 'Maria',  idade: 30}, 
     {nome: 'Teste',  idade: 52}
]

let arr2 = [
   {nome: 'Fulano', idade: 19}, 
   {nome: 'Ciclano',idade: 20}
]

const arr3 = arr1.filter( x => { 
  return JSON.stringify(arr2).indexOf(JSON.stringify(x)) < 0;
});

console.log(arr3);

  • 1

    It would have to compare the two properties, the way it is it only compares the name so if the age is different are not equal and even so.

  • It worked, I made only one change. In if(it2.nome == it1.nome) I can’t just compare the name, I need to compare the whole object, so I did if(it2 == it1). It’s okay for me to do this?

  • It will not work because they are different objects. What you need to compare are values. I made an edit on the answer by comparing the two values.

  • got it, the problem is, in my real situation, the object contains many values, over 10, and I would need everyone to be equal to make sure that that object would be the same as the arr1, has some way of identifying the object instead of doing it for example: it2.nome == it1.nome ?

  • @Leonardotheodoro ready. Corrected.

  • Sorry, but I’ll still have to use the .nome and the .idade?

  • @Leonardotheodoro Converted to string with JSON.stringify, so you can check that everything is the same.

  • That’s exactly what I needed, that way it works perfectly

Show 3 more comments

3

let arr1 = [       
 {nome: 'Fulano', idade: 19},         
 {nome: 'João',   idade: 20}, 
 {nome: 'Maria',  idade: 30},
 {nome: 'Ciclano',idade: 20},  
 {nome: 'Teste',  idade: 52}
];

let arr2 = [
  {nome: 'Ciclano',idade: 20},
  {nome: 'Fulano', idade: 19}
     
];

arr1 = arr1.filter( a1 => !arr2.filter( a2 => a1.nome == a2.nome).length);

console.log(arr1);

Explanation :

Array.filter()

within the first Array.filter it calls another Array.filter, for array 2 within this Array.filter (the second) it checks if there is the same value in array 2, so with length you get the result of if that is 0 or 1 then ! in the first Array.filter will only pick up if it is 0, in the case if the name already exists in array 2 then it will not take this value;

  • 1

    Only a brief explanation of how the Array.filter and the age check to be complete.

  • added an explanation.

2

You can use the method differenceWith library Lodash.

let arr1 = [
   {nome: 'Fulano', idade: 19}, 
   {nome: 'Fulano', idade: 39}, 
   {nome: 'Ciclano',idade: 20}, 
   {nome: 'João',   idade: 20}, 
   {nome: 'Maria',  idade: 30}, 
   {nome: 'Teste',  idade: 52}
]

let arr2 = [
   {nome: 'Fulano', idade: 19}, 
   {nome: 'Ciclano',idade: 20}
]
let arr3 = _.differenceWith(arr1, arr2, _.isEqual);
console.log(arr3)
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

2

The fastest and simplest way is to convert objects into a JSON string, compare them and filter.

But if there are other needs like validating if all attributes and values are present on both sides and even in a different order the algorithm gets more and more elaborate. And I didn’t test this example with more complex objects.

Follow an example.

let arr1 = [       
     {nome: 'Fulano', idade: 19},         
     {nome: 'João',   idade: 20}, 
     {nome: 'Maria',  idade: 30},
     {nome: 'Ciclano',idade: 20},  
     {nome: 'Teste',  idade: 52}
]

let arr2 = [
  {nome: 'Ciclano',idade: 20},
  {nome: 'Fulano', idade: 19}
         
]

var removerItems = function(arrayOriginal, arrayFiltrar) {
  var filtro = arrayOriginal.slice();  
  
  for (var i = 0; i < arrayFiltrar.length; i++) {
    var itemBusca = arrayFiltrar[i];

    for (var j = 0; j < filtro.length; j++) {
      if (JSON.stringify(filtro[j]) === JSON.stringify(itemBusca)){
        filtro.splice(j,1);        
      }
    }    
  }
  
  return filtro;
}

console.log(removerItems(arr1, arr2));

1

I created a function that takes two lists of objects and a property, how it works:

For each iteration of the first list I run the second comparing the received property, if this property does not exist in the second list then I add in a third list that after all iterations will be returned by the function.

Example:

function subtraiArray(arr1, arr2, prop){
  let arr3 = [];
  var existe;
  for(i=0;i<arr1.length;i++){
    existe = false;
    for(j=0;j<arr2.length;j++){
      if(arr1[i][prop] == arr2[j][prop]){
        existe = true;
        break;
      }
    }

    //Caso o nome dessa posição não exista no segundo array eu insiro
    if(!existe)
      arr3.push(arr1[i]);
  }
  
  return arr3;
}

let arr1 = [{nome: 'Fulano', idade: 19},{nome: 'Ciclano',idade: 20},{nome: 'João',   idade: 20},{nome: 'Maria',  idade: 30},{nome: 'Teste',  idade: 52}];

let arr2 = [{nome: 'Fulano', idade: 19},{nome: 'Ciclano',idade: 20}];

console.log(subtraiArray(arr1, arr2, "nome"));

  • And if there is another layer of objects or arrays inside the element?

  • I can do the function by receiving a list of properties instead of just one property or have them all compared, is that in his question there is no explicit need to compare for more than one property.

0

A simpler way to do it is, first, you should find the index of the element you want to remove:

var array = [2, 5, 9];
var index = array.indexOf(5);

Then you can remove the element, if found, using the splice method:

if (index > -1) {
    array.splice(index, 1);
}

In your case, as it is a two-dimensional array you can use the following function to get the index by going through the second array:

findIndex(x => x.nome == 'Ciclano')
  • But there, you have only one array, in case I have two, I need to get the index of the arr1 , I would need to know the equal element in the arr2

  • in the x.nome == 'Ciclano' how it would be done to compare the entire object of arr1 with the entire object of arr2?

  • just add one more condition: findIndex(x => x.name == 'Ciclano' && x.age == 20).

  • I understand, but two observations: 1 - You are using fixed values, the idea is q it compares with the values of the other array. (which in this case may vary) 2 - In my real situation, there are many values, not just name and age, so it is very unlikely that I will manually make value by value

  • Ok, but this is just an example. You can iterate the array and take the value inside the for like this: array[indexFor][fieldName]

Browser other questions tagged

You are not signed in. Login or sign up in order to post.