webentwicklung-frage-antwort-db.com.de

verwenden der Unterstrichmethode "difference" für Arrays von Objekten

_.difference([], [])

diese Methode funktioniert gut, wenn ich primitive Typdaten habe 

var a = [1,2,3,4];
var b = [2,5,6];

und der Aufruf von _.difference(a,b) gibt [1,3,4] zurück

aber für den fall, dass ich objekte mag 

var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];

scheint nicht zu funktionieren

36
Nas

Der Grund ist einfach, dass Objekte mit demselben Inhalt nicht dieselben Objekte sind, z.

var a = [{'id':1, 'value':10}, {'id':2, 'value':20}]; 
a.indexOf({'id':1, 'value':10})

Es wird nicht 0, sondern -1 zurückgegeben, da nach einem anderen Objekt gesucht wird

Siehe den Quellcode http://underscorejs.org/underscore.js , _.difference verwendet _.contains

_.difference = function(array) {
  var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
  return _.filter(array, function(value){ return !_.contains(rest, value); });
};

und _.contains verwendet letztendlich indexOf und findet daher keine Objekte, es sei denn, sie zeigen auf dasselbe Objekt.

Sie können den Unterstrich _.contains verbessern, indem Sie alle Elemente durchlaufen und einen Rückruf zum Vergleichen aufrufen, den Sie an difference oder contain function übergeben sollten, oder Sie können diese Version, die die contain-Methoden verbessert überprüfen

15
Anurag Uniyal

probieren Sie diese Größe aus, um den Unterschied eines Arrays von Objekten zu ermitteln:

var test = [{a: 1},{b: 2}];
var test2 = [{a: 1}];

_.filter(test, function(obj){ return !_.findWhere(test2, obj); });
53
kontr0l

Während die akzeptierte Antwort korrekt ist und die anderen Antworten ebenfalls gute Ideen liefern, gibt es eine zusätzliche Option, die mit Unterstrich ziemlich einfach zu implementieren ist.

Diese Lösung setzt voraus, dass jedes Objekt eine eindeutige ID hat. In vielen Fällen ist dies jedoch der Fall, und Sie können den Unterschied von zwei Objektarrays in nur zwei Codezeilen ermitteln.

Mit der "pluck" -Methode von Underscore können Sie schnell ein Array aller IDs in Ihrem Quellsatz und dem Zielsatz erstellen. Von dort aus funktionieren alle Arraymethoden von Unterstrich, Unterschied, Vereinigung, Schnittmenge usw.

Nach der Operation ist es einfach, die Liste der Objekte aus der gewünschten Quellliste abzurufen. Hier ist ein Beispiel:

Ausführlich:

var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];

var arr1 = _.pluck(a, "id");
var arr2 = _.pluck(b, "id");
var diff = _.difference(arr1, arr2);
var result = _.filter(a, function(obj) { return diff.indexOf(obj.id) >= 0; });

oder genauer:

var diff = _.difference(_.pluck(a, "id"), _.pluck(b, "id"));
var result = _.filter(a, function(obj) { return diff.indexOf(obj.id) >= 0; });

Natürlich kann dieselbe Technik zur Verwendung mit jeder der Array-Methoden erweitert werden.

29
Clayton Gulick
without using underscorejs,
here is the pretty simple method i got solution ... 

a = [{'key':'123'},{'key':'222'},{'key':'333'}]
b = [{'key':'123'},{'key':'222'}]

var diff = a.filter(function(item1) {
  for (var i in b) {
    if (item1.key === b[i].key) { return false; }
  };
  return true;
});
console.log('result',diff)

Ich kann mir Situationen vorstellen, in denen ich eher den Ansatz @ kontr0l als etwas anderes verwenden würde, aber Sie müssen verstehen, dass dieser Ansatz quadratisch ist. Daher ist dieser Code im Wesentlichen eine Abstraktion für naiven Ansatz - iterieren Sie alle Werte in zwei Arrays. 

Es gibt Ansätze, die besser als quadratisch sind, ich verwende hier keine große O-Notation, aber hier sind zwei Hauptansätze, beide sind besser als naive:

  • iterieren Sie durch eines der Arrays und prüfen Sie das Vorhandensein im sortierten zweiten Array mithilfe der binären Suche.
  • setze Werte in set/hash/dictionary/du nennst es.

Wie bereits erwähnt, kann der erste Ansatz für Objekte verwendet werden, wenn Sie die Standardmethode difference mit einer flexibleren Analogie der Methode indexOf neu implementieren. 

Mit dem zweiten Ansatz können wir mit der Tatsache an die Wand gehen, dass ab Februar 2015 nur noch moderne Browser Sets unterstützen. Bei Hashes (na ja, Objekten) in Javascript können sie nur Schlüssel vom Typ "Stringtyp" haben, sodass jedes als Schlüssel zuerst aufgerufene Objekt über die toString-Methode konvertiert werden sollte. Also müssen wir etwas Korrespondenz bereitstellen. In der Praxis ist dies in den meisten Fällen ziemlich unkompliziert. Für Ihr spezielles Beispiel kann eine solche Korrespondenz nur String(obj.id) sein.

Bei einer solchen Korrespondenz können wir auch die folgende Herangehensweise verwenden:

var idsA = _.pluck(a, 'id');
var idsB = _.pluck(b, 'id');

// actually here we can stop in some cases, because 
// quite often we need to identify object, but not the object itself - 
// for instance to send some ids through remote API.
var intersect = _.intersection(idsA, idsB);

//to be 100% sure you get the idea, here we assume that object having equal ids are treated as equal, so does not really matter which of arrays we'll iterate:

var dictA = _.object(idsA, a); // now we can find a by id faster then with _.find
var intersectObj = intersect.map(function(id) {return dictA[id})

Wenn wir jedoch eine etwas strengere Einschränkung zulassen - dass wir eine Übereinstimmung zwischen unseren gesetzten Objekten und natürlichen Zahlen herstellen können, können wir einen noch effizienteren Algorithmus erstellen, d. H. Alle unsere IDs sind nicht negative Ganzzahlen - wir können einen effizienteren Algorithmus verwenden. 

Der Trick besteht darin, set durch Einführung zweier Helper-Arrays auf folgende Weise zu implementieren:

var naturalSet = function (arr) {
    var sparse = [];
    var dense = [];

    var contains = function (i) {
        var res = sparse[i] < dense.length && dense[sparse[i]] == i;
        return res;
    }

    var add = function (v) {
        if (!contains(v)) {
            sparse[v] = dense.length;
            dense.Push(v);
        }
    }

    arr.forEach(add);

    return {
        contains: contains,
        toArray: function () {
            return dense
        },
        _getDense: function () {
            return dense
        },
        _getSparse: function () {
            return sparse
        }
    }
}

Dann können wir set mit Mapping auf naturalSet einführen:

var set = function (arr, valueOf) {
    var natSet = naturalSet(arr.map(valueOf));
    return {
        contains: function (item) {
            return natSet.contains(valueOf(item))
        },
        toArray: function () {
            var sparse = natSet._getSparse();
            var res = natSet._getDense().map(function (i) {
                return arr[sparse[i]];
            });
            return res;
        }
    }
}

und schließlich können wir die Kreuzung einführen:

var intersection = function(arr1, arr2, valueOf) {
   return set(arr2.filter(set(arr1, valueOf).contains), valueOf).toArray();
}

Wenn Sie sich also auf die Datenstruktur verlassen, die Sie gerade bearbeiten, kann dies manchmal hilfreich sein.

3
shabunc
var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];

var c = _.difference(a.map(e => e.id), b.map(e =>e.id));
var array = [];
array = a.map(e => {
   if(c.includes(e.id)){
     return e;
   }
}).filter(r=>r);
1
Anirudh

Verstehe nicht, warum diese Antworten so komplex sind, wenn mir etwas fehlt?

var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];

// Or use lodash _.differenceBy
const difference = (array1, array2, prop = 'id') =>
  array1.filter(item1 =>
    !array2.some(item2 =>
      item2[prop] === item1[prop],
    ),
  );
  
// In one array.
console.log(difference(a, b));

// Intersection.
console.log([...difference(a, b), ...difference(b, a)]);

0
Dominic

Verzeihen Sie mir, dass ich spät hier hingehe, aber das kann helfen:

array_of_objects = 
    // return the non-matching items (without the expected properties)
    _.difference(array_of_objects,
        // filter original list for items with expected properties
        _.where(
            // original list
            array_of_objects,
            // expected properties
            {'id':1, 'value':10}
        )
    )
0
Gauss156