it-swarm.com.de

Wie Sie den Unterschied zwischen zwei Arrays von Objekten in JavaScript ermitteln können

Ich habe zwei Ergebnissätze wie folgt:

// 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"},
    { value="644838b3-604d-4899-8b78-09e4799f586f", display="Muhammed"},
    { value="b6ee537a-375c-45bd-b9d4-4dd84a75041d", display="Ravi"},
    { value="e97339e1-939d-47ab-974c-1b68c9cfb536", display="Ajmal"}
]

Das Endergebnis, das ich brauche, ist der Unterschied zwischen diesen Arrays. 

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

Kann man so etwas in JavaScript machen?

58
BKM

Wenn Sie nur native JS verwenden, wird so etwas funktionieren:

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

97
Cerbrus

Sie könnten Array.prototype.filter() in Kombination mit Array.prototype.some() verwenden.

Hier ein Beispiel (vorausgesetzt, Ihre Arrays werden in den Variablen result1 und result2 gespeichert):

//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);
44
kaspermoerch

Ich gehe etwas allgemeiner vor, wenn auch in den Ideen den Ansätzen von @Cerbrus und @Kasper Moerch. Ich erstelle eine Funktion, die ein Prädikat akzeptiert, um zu bestimmen, ob zwei Objekte gleich sind (hier ignorieren wir die $$hashKey-Eigenschaft, es könnte jedoch alles sein) und gibt eine Funktion zurück, die die symmetrische Differenz zweier Listen basierend auf diesem Prädikat berechnet:

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

Es hat einen geringfügigen Vorteil gegenüber Cerebrus 'Ansatz (wie auch Kasper Moerch' Ansatz), da es frühzeitig entkommt. Wenn es eine Übereinstimmung findet, stört es den Rest der Liste nicht. Wenn ich eine curry-Funktion zur Verfügung hätte, würde ich dies etwas anders machen, aber das funktioniert gut.

Erläuterung

In einem Kommentar wurde nach einer detaillierteren Erklärung für Anfänger gefragt. Hier ist ein Versuch.

Wir übergeben die folgende Funktion an makeSymmDiffFunc:

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

Mit dieser Funktion entscheiden wir, dass zwei Objekte gleich sind. Wie alle Funktionen, die true oder false zurückgeben, kann sie als "Prädikatfunktion" bezeichnet werden. Dies ist jedoch nur eine Terminologie. Der Hauptpunkt ist, dass makeSymmDiffFunc mit einer Funktion konfiguriert ist, die zwei Objekte akzeptiert und true zurückgibt, wenn wir sie als gleich betrachten, false, wenn wir dies nicht tun.

makeSymmDiffFunc (read "make symmetric difference function") gibt uns eine neue Funktion zurück:

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

Dies ist die Funktion, die wir tatsächlich verwenden werden. Wir übergeben es zwei Listen und es findet die Elemente in der ersten nicht in der zweiten, dann die in der zweiten nicht in der ersten und kombinieren diese beiden Listen.

Wenn ich es noch einmal anschaue, hätte ich definitiv einen Hinweis aus Ihrem Code nehmen und die Hauptfunktion mit some ein wenig vereinfachen können:

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 verwendet das Prädikat und gibt die Elemente der ersten Liste nicht in der zweiten zurück. Dies ist einfacher als mein erster Durchlauf mit einer separaten contains-Funktion.

Schließlich wird die Hauptfunktion in einen sofort aufgerufenen Funktionsausdruck (IIFE) eingeschlossen, um die interne complement-Funktion außerhalb des globalen Bereichs zu halten.


Update, einige Jahre später

Nun, da ES2015 mittlerweile allgegenwärtig ist, würde ich dieselbe Technik mit viel weniger Boilerplate vorschlagen:

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"}
14
Scott Sauyet
import differenceBy from 'lodash/differenceBy'

const myDifferences = differenceBy(Result1, Result2, 'value')

Dadurch wird der Unterschied zwischen zwei Arrays von Objekten mit der Taste value zum Vergleich zurückgegeben. Beachten Sie, dass zwei Dinge mit demselben Wert nicht zurückgegeben werden, da die anderen Schlüssel ignoriert werden.

Dies ist ein Teil von lodash .

4
Noah

Sie können ein Objekt mit Schlüsseln als eindeutigen Wert für jedes Objekt im Array erstellen und dann jedes Array anhand des Vorhandenseins des Schlüssels im Objekt des anderen filtern. Es reduziert die Komplexität der Operation.

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

ES5

var 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"}];
var 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 valuesA = a.reduce(function(a,c){a[c.value] = c.value; return a; }, {});
var valuesB = b.reduce(function(a,c){a[c.value] = c.value; return a; }, {});
var result = a.filter(function(c){ return !valuesB[c.value]}).concat(b.filter(function(c){ return !valuesA[c.value]}));
console.log(result);

4
Nikhil Aggarwal

Ich denke, dass die @ Cerbrus-Lösung genau richtig ist. Ich habe die gleiche Lösung implementiert, aber den wiederholten Code in seine eigene Funktion (DRY) extrahiert. 

 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;
  });
}
3
Michael

Für diejenigen, die One-Liner-Lösungen in ES6 mögen:

const arrayOne = [ 
  { 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" },
];
          
const arrayTwo = [
  { 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"},
];

const results = arrayOne.filter(({ value: id1 }) => !arrayTwo.some(({ value: id2 }) => id2 === id1));

console.log(results);
2
atheane

Ich habe diese Lösung mit Filter und einigen gefunden.

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

0
Gorez Tony

Die meisten Antworten sind ziemlich komplex, aber ist die Logik dahinter nicht einfach? 

  1. prüfen Sie, welches Array länger ist, und geben Sie es als ersten Parameter an (wenn Länge gleich ist, spielt die Reihenfolge der Parameter keine Rolle).
  2. Iteriere über array1.
  3. Überprüfen Sie für das aktuelle Iterationselement von array1, ob es in array2 vorhanden ist
  4. Wenn es NICHT vorhanden ist, dann
  5. Schieben Sie es auf "difference" -Array
const getArraysDifference = (longerArray, array2) => {
  const difference = [];

  longerArray.forEach(el1 => {      /*1*/
    el1IsPresentInArr2 = array2.some(el2 => el2.value === el1.value); /*2*/

    if (!el1IsPresentInArr2) { /*3*/
      difference.Push(el1);    /*4*/
    }
  });

  return difference;
}

O (n ^ 2) -Komplexität.

0
azrahel

Ich habe ein verallgemeinertes Diff erstellt, das 2 Objekte jeder Art vergleicht und einen Modifikations-Handler ausführen kann Gist.github.com/bortunac "diff.js" ein Ex der Verwendung von:

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

die Eigenschaft a wird geändert, b wird gelöscht, c wird geändert, d wird hinzugefügt

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

}

verwenden Sie jetzt wie

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

die Konsole wird angezeigt

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