Come ottenere la differenza tra due matrici di oggetti in JavaScript

Ho due set di risultati come questo:

// 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" } ] 

Il risultato finale di cui ho bisogno è la differenza tra questi array: il risultato finale dovrebbe essere così:

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

È ansible fare qualcosa di simile in JavaScript?

Usando solo JS nativo, qualcosa del genere funzionerà:

 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); 

È ansible utilizzare Array.prototype.filter() in combinazione con Array.prototype.some() .

Ecco un esempio (supponendo che gli array siano memorizzati nelle variabili 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); 

Prendo un approccio leggermente più generale, sebbene simile nelle idee agli approcci di @Cerbrus e @Kasper Moerch . Creo una funzione che accetta un predicato per determinare se due oggetti sono uguali (qui ignoriamo la proprietà $$hashKey , ma potrebbe essere qualsiasi cosa) e restituiamo una funzione che calcola la differenza simmetrica di due elenchi in base a tale predicato:

 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"} 

Ha un piccolo vantaggio rispetto all’approccio di Cerebrus (come l’approccio di Kasper Moerch) in quanto sfugge presto; se trova una corrispondenza, non si preoccupa di controllare il resto della lista. Se avessi una funzione curry portata di mano, lo farei in modo leggermente diverso, ma funziona bene.

Spiegazione

Un commento ha chiesto una spiegazione più dettagliata per i principianti. Ecco un tentativo.

Passiamo la seguente funzione per makeSymmDiffFunc :

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

Questa funzione è il modo in cui decidiamo che due oggetti sono uguali. Come tutte le funzioni che restituiscono true o false , può essere chiamato “funzione di predicato”, ma questa è solo terminologia. Il punto principale è che makeSymmDiffFunc è configurato con una funzione che accetta due oggetti e restituisce true se li consideriamo uguali, false se non lo facciamo.

Usando questo, makeSymmDiffFunc (leggi “make simmetric difference function”) ci restituisce una nuova funzione:

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

Questa è la funzione che useremo effettivamente. Gli passiamo due liste e trova gli elementi nel primo non nel secondo, poi nel secondo non nel primo e combinano questi due elenchi.

Guardandoci sopra, però, avrei sicuramente potuto prendere spunto dal tuo codice e semplificato la funzione principale un po ‘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 utilizza il predicato e restituisce gli elementi della sua prima lista non nel suo secondo. Questo è più semplice del mio primo passaggio con una funzione contains separata.

Infine, la funzione principale è racchiusa in un’espressione di funzione immediatamente invocata ( IIFE ) per mantenere la funzione di complement interno fuori dall’ambito globale.


Aggiornamento, alcuni anni dopo

Ora che ES2015 è diventato abbastanza onnipresente, suggerirei la stessa tecnica, con un numero molto minore di piastre:

 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"} 

Penso che la soluzione @Cerbrus sia azzeccata. Ho implementato la stessa soluzione ma ho estratto il codice ripetuto nella sua funzione (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') 

Ciò restituirà la differenza tra due matrici di oggetti, utilizzando il value chiave per confrontarli. Nota che due cose con lo stesso valore non saranno restituite, poiché le altre chiavi sono ignorate.

Questa è una parte di lodash .

È ansible creare un object con le chiavi come valore univoco corrispondente per ciascun object nella matrice e quindi filtrare ciascun array in base all’esistenza della chiave nell’object di un altro. Riduce la complessità dell’operazione.

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); 

Ho trovato questa soluzione usando il filtro e alcuni.

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

Ho fatto un diff diffuso che confronta 2 oggetti di qualsiasi tipo e può eseguire un gestore di modifica gist.github.com/bortunac “diff.js” un ex di utilizzare:

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

quindi la proprietà a viene modificata, b viene eliminato, c modificato, d viene aggiunto

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

}

ora usa come

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

la console mostrerà

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

Se si desidera utilizzare librerie esterne, è ansible utilizzare _.difference in underscore.js per ottenere ciò. _.difference restituisce i valori dalla matrice che non sono presenti negli altri array.

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