Unterschied zwischen zwei Listen


118

Ich habe zwei generische Listen mit CustomsObjects gefüllt.

Ich muss den Unterschied zwischen diesen beiden Listen (Elemente, die in der ersten ohne die Elemente in der zweiten sind) in einer dritten Liste abrufen.

Ich dachte, die Verwendung wäre .Except()eine gute Idee, aber ich sehe nicht, wie ich sie verwenden soll. Hilfe!

c# 

Antworten:


237

Verwenden Exceptist genau der richtige Weg. Wenn Ihr Typ überschreibt Equalsund GetHashCodeoder Sie nur an der Gleichheit des Referenztyps interessiert sind (dh zwei Referenzen sind nur "gleich", wenn sie sich auf genau dasselbe Objekt beziehen), können Sie einfach Folgendes verwenden:

var list3 = list1.Except(list2).ToList();

Wenn Sie eine benutzerdefinierte Idee der Gleichheit ausdrücken müssen, z. B. nach ID, müssen Sie diese implementieren IEqualityComparer<T>. Beispielsweise:

public class IdComparer : IEqualityComparer<CustomObject>
{
    public int GetHashCode(CustomObject co)
    {
        if (co == null)
        {
            return 0;
        }
        return co.Id.GetHashCode();
    }

    public bool Equals(CustomObject x1, CustomObject x2)
    {
        if (object.ReferenceEquals(x1, x2))
        {
            return true;
        }
        if (object.ReferenceEquals(x1, null) ||
            object.ReferenceEquals(x2, null))
        {
            return false;
        }
        return x1.Id == x2.Id;
    }
}

Dann benutze:

var list3 = list1.Except(list2, new IdComparer()).ToList();

Beachten Sie, dass dadurch alle doppelten Elemente entfernt werden. Wenn Sie Duplikate benötigen, um diese zu erhalten, ist es wahrscheinlich am einfachsten, ein Set daraus zu erstellen list2und Folgendes zu verwenden:

var list3 = list1.Where(x => !set2.Contains(x)).ToList();

18
Nebenbei bemerkt, würde ich hinzufügen , dass Excepta Set Betrieb, dann wird die resultierende Liste unterschiedliche Werte haben, zB {'A','A','B','C'}.Except({'B','C'}) Erträge{'A'}
digEmAll

4
Ich habe folgendes: {'A', 'A', 'B', 'C'}. Außer ({'B', 'C'}) gibt {'A'} zurück, was funktioniert ... aber {'B' , 'C'}. Außer ({'A', 'B', 'C'}) gibt NICHT {'A'} zurück
MrLister

2
Die Satzdifferenz von zwei Sätzen ist definiert als die Mitglieder des ersten Satzes, die nicht im zweiten Satz erscheinen (unter Angabe von MS). {B, C} .Except ({A, B, C}) sollte also nichts zurückgeben, da B und C beide im zweiten Satz enthalten sind. Kein Fehler, mehr mit der mathematischen Definition der Funktion zu tun.
Steve Hibbert

Also @JonSkeet, wenn ich zwei Listen auf der Grundlage von 2 Eigenschaften vergleichen möchte, kann ich auf diese Weise einen Vergleich schreiben und die Elemente erhalten, die nicht gleich sind, oder?
Ehsan Sajjad

1
@NetMage: Das OP gab an, dass sie "Elemente, die im ersten sind, ohne die Elemente im zweiten" wollen - das klingt für mich nach einem festgelegten Unterschied. Wenn die erste Liste {5, 5, 5, 5, 1} enthält und die zweite Liste {5} enthält, befindet sich nur 1 in der ersten Liste, nicht jedoch in der zweiten.
Jon Skeet

72

Sie könnten so etwas tun:

var result = customlist.Where(p => !otherlist.Any(l => p.someproperty == l.someproperty));

es hat mir eine foreach-Schleife erspart.
Alice7

12
Sollte eines dieser "l.someproperty" nicht "p.someproperty" sein?
Manos Dilaverakis

6
Dies kann mit customlist.Where (p => otherlist.Any (l => p.someproperty! = L.someproperty)) vereinfacht werden.
Dhanuka777

@ Dhanuka777 das ist falsch. Das Anysollte ein sein, Allwenn du es so machen willst. Dies liegt daran, dass in der zweiten Liste möglicherweise das Element aus der ersten Liste enthalten ist. Wenn es jedoch nicht das erste ist, das überprüft wird, wird es sofort als wahr eingestuft p.someproperty != l.someproperty. Dies führt dazu, dass Elemente zurückgegeben werden, die in beiden Listen vorhanden sind. Schande über die 6 Leute, die dies positiv bewertet haben.
Murphybro2

26

Ich denke, es ist wichtig zu betonen, dass Sie mit der Except-Methode nur Elemente zurückgeben, die im ersten ohne die Elemente im zweiten sind. Es werden nicht die Elemente in der zweiten zurückgegeben, die nicht in der ersten erscheinen.

var list1 = new List<int> { 1, 2, 3, 4, 5};
var list2 = new List<int> { 3, 4, 5, 6, 7 };

var list3 = list1.Except(list2).ToList(); //list3 contains only 1, 2

Aber wenn Sie einen echten Unterschied zwischen zwei Listen erzielen möchten:

Artikel, die im ersten ohne die Artikel im zweiten sind, und Artikel, die im zweiten sind, ohne die Artikel im ersten.

Sie müssen Außer zweimal verwenden:

var list1 = new List<int> { 1, 2, 3, 4, 5};
var list2 = new List<int> { 3, 4, 5, 6, 7 };

var list3 = list1.Except(list2); //list3 contains only 1, 2
var list4 = list2.Except(list1); //list4 contains only 6, 7
var resultList = list3.Concat(list4).ToList(); //resultList contains 1, 2, 6, 7

Oder Sie können die SymmetricExceptWith- Methode von HashSet verwenden. Aber es ändert das Set, auf dem aufgerufen wird:

var list1 = new List<int> { 1, 2, 3, 4, 5};
var list2 = new List<int> { 3, 4, 5, 6, 7 };

var list1Set = list1.ToHashSet(); //.net framework 4.7.2 and .net core 2.0 and above otherwise new HashSet(list1)
list1Set.SymmetricExceptWith(list2);
var resultList = list1Set.ToList(); //resultList contains 1, 2, 6, 7

Das ist hilfreich. Seien Sie sich nur bewusst, dass das OP klargestellt hat, dass sie keinen vollständigen Unterschied wollen: "Ich muss den Unterschied zwischen diesen beiden Listen abrufen ( Elemente, die in der ersten sind, ohne die Elemente in der zweiten )".
Rsenna

10
var third = first.Except(second);

(Sie können auch ToList()nach anrufen Except(), wenn Sie nicht gerne auf faule Sammlungen verweisen.)

Das Except()Verfahren vergleicht die Werte der Standardvergleich verwendet wird , wenn die Werte verglichen werden , sind von Basisdatentypen, wie beispielsweise int, string, decimalusw.

Andernfalls wird der Vergleich anhand der Objektadresse durchgeführt, was wahrscheinlich nicht das ist, was Sie möchten. In diesem Fall lassen Sie Ihre benutzerdefinierten Objekte implementieren IComparable(oder implementieren Sie eine benutzerdefinierte IEqualityComparerund übergeben Sie sie an die Except()Methode).


3
var list3 = list1.Where(x => !list2.Any(z => z.Id == x.Id)).ToList();

Hinweis: list3Enthält die Elemente oder Objekte, die nicht in beiden Listen enthalten sind. Hinweis: Es ist ToList()nichttoList()


1

Da die Except-Erweiterungsmethode mit zwei IEumerables arbeitet, scheint es mir, dass es sich um eine O (n ^ 2) -Operation handelt. Wenn die Leistung ein Problem darstellt (wenn Ihre Listen groß sind), würde ich empfehlen, ein HashSet aus list1 zu erstellen und die ExceptWith-Methode von HashSet zu verwenden.


9
Enumerable.Exceptintern verwendet HashSetoder ähnliches. Der naive O (n ^ 2) -Algorithmus wird definitiv nicht verwendet.
Jim Mischel

@ Jim Mischel: Sie haben Recht, Enumerable.Except verwendet eine interne Set-Datenstruktur und fügt dem Set Elemente aus beiden IEnumerables hinzu. Ich wünschte, die Dokumentation würde etwas dazu sagen.
Foson

1

Hier ist meine Lösung:

    List<String> list1 = new List<String>();

    List<String> list2 = new List<String>();

    List<String> exceptValue = new List<String>();

foreach(String L1 in List1) 
{
    if(!List2.Contains(L1)
    {
         exceptValue.Add(L1);
    }
}
foreach(String L2 in List2) 
{
    if(!List1.Contains(L2)
    {
         exceptValue.Add(L2);
    }
}

-1

etwas spät, aber hier ist eine funktionierende Lösung für mich

 var myBaseProperty = (typeof(BaseClass)).GetProperties();//get base code properties
                    var allProperty = entity.GetProperties()[0].DeclaringType.GetProperties();//get derived class property plus base code as it is derived from it
                    var declaredClassProperties = allProperty.Where(x => !myBaseProperty.Any(l => l.Name == x.Name)).ToList();//get the difference

Im oben genannten Code erhalte ich den Eigenschaftsunterschied zwischen meiner Basisklasse und der abgeleiteten Klassenliste


-1
var resultList = checklist.Where(p => myList.All(l => p.value != l.value)).ToList();

Hinweis: Ergebnisliste ist die Elemente, die in der Checkliste, aber nicht in myList
Teezy7

-2
List<ObjectC> _list_DF_BW_ANB = new List<ObjectC>();    
List<ObjectA> _listA = new List<ObjectA>();
List<ObjectB> _listB = new List<ObjectB>();

foreach (var itemB in _listB )
{     
    var flat = 0;
    foreach(var itemA in _listA )
    {
        if(itemA.ProductId==itemB.ProductId)
        {
            flat = 1;
            break;
        }
    }
    if (flat == 0)
    {
        _list_DF_BW_ANB.Add(itemB);
    }
}

6 Jahre nach akzeptierter Antwort und fügt nichts Neues hinzu
Mitch Wheat

-3

Wenn beide Listen eine IEnumerable-Schnittstelle implementieren, können Sie dies mit LINQ erreichen.

list3 = list1.where(i => !list2.contains(i));

6
Und wenn Ihre Listen jeweils eine Million Elemente enthalten, werden Sie lange warten. Verwenden Sie IEnumerable.Exceptstattdessen.
Jim Mischel

3
Ja. Ich habe nur ungefähr 450 Artikel und warte immer noch. Seien Sie vorsichtig damit.
Marnee KG7SIO

-3
        List<int> list1 = new List<int>();
        List<int> list2 = new List<int>();
        List<int> listDifference = new List<int>();

        foreach (var item1 in list1)
        {
            foreach (var item2 in list2)
            {
                if (item1 != item2)
                    listDifference.Add(item1);
            }
        }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.