Como obter a diferença entre duas matrizes de objects em JavaScript

Eu tenho dois conjuntos de resultados como este:

// Result 1 [ { value="4a55eff3-1e0d-4a81-9105-3ddd7521d642", display="Jamsheer" }, { value="644838b3-604d-4899-8b78-09e4799f586f", display="Muhammed" }, { value="b6ee537a-375c-45bd-b9d4-4dd84a75041d", display="Ravi" }, { value="e97339e1-939d-47ab-974c-1b68c9cfb536", display="Ajmal" }, { value="a63a6f77-c637-454e-abf2-dfb9b543af6c", display="Ryan" } ] // Result 2 [ { value="4a55eff3-1e0d-4a81-9105-3ddd7521d642", display="Jamsheer", $$hashKey="008" }, { value="644838b3-604d-4899-8b78-09e4799f586f", display="Muhammed", $$hashKey="009" }, { value="b6ee537a-375c-45bd-b9d4-4dd84a75041d", display="Ravi", $$hashKey="00A" }, { value="e97339e1-939d-47ab-974c-1b68c9cfb536", display="Ajmal", $$hashKey="00B" } ] 

O resultado final que eu preciso é a diferença entre esses arrays – o resultado final deve ser assim:

 [{ value="a63a6f77-c637-454e-abf2-dfb9b543af6c", display="Ryan" }] 

É possível fazer algo assim em JavaScript?

Usando apenas o JS nativo, algo assim funcionará:

 a = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal"}, { value:"a63a6f77-c637-454e-abf2-dfb9b543af6c", display:"Ryan"}] b = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer", $$hashKey:"008"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed", $$hashKey:"009"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi", $$hashKey:"00A"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal", $$hashKey:"00B"}] function comparer(otherArray){ return function(current){ return otherArray.filter(function(other){ return other.value == current.value && other.display == current.display }).length == 0; } } var onlyInA = a.filter(comparer(b)); var onlyInB = b.filter(comparer(a)); result = onlyInA.concat(onlyInB); console.log(result); 

Você poderia usar Array.prototype.filter() em combinação com Array.prototype.some() .

Aqui está um exemplo (supondo que seus arrays são armazenados nas variables result1 e result2 ):

 //Find values that are in result1 but not in result2 var uniqueResultOne = result1.filter(function(obj) { return !result2.some(function(obj2) { return obj.value == obj2.value; }); }); //Find values that are in result2 but not in result1 var uniqueResultTwo = result2.filter(function(obj) { return !result1.some(function(obj2) { return obj.value == obj2.value; }); }); //Combine the two arrays of unique entries var result = uniqueResultOne.concat(uniqueResultTwo); 

Adoto uma abordagem um pouco mais genérica, embora semelhante em idéias às abordagens de @Cerbrus e @Kasper Moerch . Eu crio uma function que aceita um predicado para determinar se dois objects são iguais (aqui nós ignoramos a propriedade $$hashKey , mas poderia ser qualquer coisa) e retornamos uma function que calcula a diferença simétrica de duas listas baseadas naquele predicado:

 a = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal"}, { value:"a63a6f77-c637-454e-abf2-dfb9b543af6c", display:"Ryan"}] b = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer", $$hashKey:"008"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed", $$hashKey:"009"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi", $$hashKey:"00A"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal", $$hashKey:"00B"}] var makeSymmDiffFunc = (function() { var contains = function(pred, a, list) { var idx = -1, len = list.length; while (++idx < len) {if (pred(a, list[idx])) {return true;}} return false; }; var complement = function(pred, a, b) { return a.filter(function(elem) {return !contains(pred, elem, b);}); }; return function(pred) { return function(a, b) { return complement(pred, a, b).concat(complement(pred, b, a)); }; }; }()); var myDiff = makeSymmDiffFunc(function(x, y) { return x.value === y.value && x.display === y.display; }); var result = myDiff(a, b); //=> {value="a63a6f77-c637-454e-abf2-dfb9b543af6c", display="Ryan"} 

Tem uma pequena vantagem sobre a abordagem da Cerebrus (assim como a abordagem de Kasper Moerch) na medida em que escapa cedo; se encontrar uma correspondência, não se preocupará em verificar o resto da lista. Se eu tivesse uma function de curry mão, faria isso de forma um pouco diferente, mas isso funciona bem.

Explicação

Um comentário pediu uma explicação mais detalhada para iniciantes. Aqui está uma tentativa.

Nós passamos a seguinte function para makeSymmDiffFunc :

 function(x, y) { return x.value === y.value && x.display === y.display; } 

Esta function é como decidimos que dois objects são iguais. Como todas as funções que retornam true ou false , ele pode ser chamado de “function de predicado”, mas isso é apenas terminologia. O ponto principal é que makeSymmDiffFunc é configurado com uma function que aceita dois objects e retorna true se os consideramos iguais, false se não os considerarmos.

Usando isso, makeSymmDiffFunc (leia “make symmetric difference function”) nos retorna uma nova function:

  return function(a, b) { return complement(pred, a, b).concat(complement(pred, b, a)); }; 

Esta é a function que vamos usar. Passamos duas listas e encontramos os elementos no primeiro não no segundo, depois os no segundo não no primeiro e combinamos essas duas listas.

Olhando para cima novamente, no entanto, eu definitivamente poderia ter tomado uma sugestão do seu código e simplificado bastante a function principal usando some :

 var makeSymmDiffFunc = (function() { var complement = function(pred, a, b) { return a.filter(function(x) { return !b.some(function(y) {return pred(x, y);}); }); }; return function(pred) { return function(a, b) { return complement(pred, a, b).concat(complement(pred, b, a)); }; }; }()); 

complement usa o predicado e retorna os elementos de sua primeira lista não em seu segundo. Isso é mais simples do que meu primeiro passo com uma function de contains separada.

Finalmente, a function principal é envolvida em uma expressão de function ( IIFE ) imediatamente invocada para manter a function de complement interna fora do escopo global.


Atualização, alguns anos depois

Agora que o ES2015 se tornou bastante onipresente, eu sugeriria a mesma técnica, com muito menos clichê:

 const diffBy = (pred) => (a, b) => a.filter(x => !b.some(y => pred(x, y))) const makeSymmDiffFunc = (pred) => (a, b) => diffBy(pred)(a, b).concat(diffBy(pred)(b, a)) const myDiff = makeSymmDiffFunc((x, y) => x.value === y.value && x.display === y.display) const result = myDiff(a, b) //=> {value="a63a6f77-c637-454e-abf2-dfb9b543af6c", display="Ryan"} 

Eu acho que a solução @Cerbrus está no ponto. Eu implementei a mesma solução, mas extraí o código repetido em sua própria function (DRY).

  function filterByDifference(array1, array2, compareField) { var onlyInA = differenceInFirstArray(array1, array2, compareField); var onlyInb = differenceInFirstArray(array2, array1, compareField); return onlyInA.concat(onlyInb); } function differenceInFirstArray(array1, array2, compareField) { return array1.filter(function (current) { return array2.filter(function (current_b) { return current_b[compareField] === current[compareField]; }).length == 0; }); } 
 import differenceBy from 'lodash/differenceBy' const myDifferences = differenceBy(Result1, Result2, 'value') 

Isso retornará a diferença entre dois arrays de objects, usando o value da chave para compará-los. Observe que duas coisas com o mesmo valor não serão retornadas, pois as outras chaves são ignoradas.

Isso é uma parte do lodash .

Você pode criar um object com chaves como o valor único correspondente para cada object na matriz e, em seguida, filtrar cada matriz com base na existência da chave no object de outra pessoa. Reduz a complexidade da operação.

ES6

 let a = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal"}, { value:"a63a6f77-c637-454e-abf2-dfb9b543af6c", display:"Ryan"}]; let b = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer", $$hashKey:"008"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed", $$hashKey:"009"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi", $$hashKey:"00A"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal", $$hashKey:"00B"}]; let valuesA = a.reduce((a,{value}) => Object.assign(a, {[value]:value}), {}); let valuesB = b.reduce((a,{value}) => Object.assign(a, {[value]:value}), {}); let result = [...a.filter(({value}) => !valuesB[value]), ...b.filter(({value}) => !valuesA[value])]; console.log(result); 

Eu fiz um diff generalizado que compara 2 objects de qualquer tipo e pode executar um manipulador de modificação gist.github.com/bortunac “diff.js” um ex de usar:

 old_obj={a:1,b:2,c:[1,2]} now_obj={a:2 , c:[1,3,5],d:55} 

então a propriedade a é modificada, b é deletada, c modificada, d é adicionada

 var handler=function(type,pointer){ console.log(type,pointer,this.old.point(pointer)," | ",this.now.point(pointer)); 

}

agora use como

 df=new diff(); df.analize(now_obj,old_obj); df.react(handler); 

o console mostrará

 mdf ["a"] 1 | 2 mdf ["c", "1"] 2 | 3 add ["c", "2"] undefined | 5 add ["d"] undefined | 55 del ["b"] 2 | undefined 

Eu encontrei esta solução usando filtro e alguns.

  resultFilter = (firstArray, secondArray) => { return firstArray.filter(firstArrayItem => !secondArray.some( secondArrayItem => firstArrayItem._user === secondArrayItem._user ) ); }; 

Se você estiver disposto a usar bibliotecas externas, você pode usar _.difference in underscore.js para conseguir isso. _.difference retorna os valores da matriz que não estão presentes nas outras matrizes.

 _.difference([1,2,3,4,5][1,4,10]) ==>[2,3,5]