İki liste arasındaki fark


118

CustomsObjects ile doldurulmuş iki genel listem var.

Üçüncü bir listedeki bu iki liste arasındaki farkı (ikinci listedeki öğeler olmadan ilkinde olan öğeler) almam gerekiyor.

Kullanmanın .Except()iyi bir fikir olduğunu düşünüyordum ama bunu nasıl kullanacağımı bilmiyorum .. Yardım!

c# 

Yanıtlar:


237

Kullanmak Excepttam olarak doğru yoldur. Türünüz geçersiz kılıyorsa Equalsve GetHashCodeveya yalnızca referans türü eşitliğiyle ilgileniyorsanız (yani, iki referans yalnızca aynı nesneye atıfta bulunuyorlarsa "eşittir"), yalnızca şunları kullanabilirsiniz:

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

Özel bir eşitlik fikrini ifade etmeniz gerekirse, örneğin kimlik ile, uygulamanız gerekir IEqualityComparer<T>. Örneğin:

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

Sonra kullan:

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

Bunun yinelenen öğeleri kaldıracağını unutmayın. Korunacak kopyalara ihtiyacınız varsa, aşağıdakilerden bir set oluşturmak list2ve kullanmak muhtemelen en kolay yoldur :

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

18
Bir kenara, Exceptbunun bir Set işlemi olduğunu ekleyeceğim , sonra ortaya çıkan liste farklı değerlere sahip olacak, örneğin {'A','A','B','C'}.Except({'B','C'}) döner{'A'}
digEmAll

4
Şuna sahibim: {'A', 'A', 'B', 'C'}. ({'B', 'C'}) dışında {'A'} döndürür ... ancak {'B' , 'C'}. ({'A', 'B', 'C'}) hariç, {'A'}
DÖNÜŞMEZ

2
İki setin set farkı, ikinci sette görünmeyen birinci setin üyeleri olarak tanımlanır (MS'yi alıntılar). Öyleyse, {B, C}. ({A, B, C}) hariç hiçbir şey döndürmemelidir, çünkü hem B hem de C ikinci kümede. Hata değil, daha çok fonksiyonun matematiksel tanımıyla ilgili.
Steve Hibbert

Yani @JonSkeet diyelim ki 2 özellik bazında iki listeyi karşılaştırmak istersem bu şekilde karşılaştırıcı yazıp eşit olmayan öğeleri elde edebilir miyim?
Ehsan Sajjad

1
@NetMage: OP, "İkincideki öğeler olmadan ilkinde olan öğeler" istediğini belirtti - bu bana belirli bir fark gibi geliyor. İlk liste {5, 5, 5, 5, 1} içeriyorsa ve ikinci liste {5} içeriyorsa, bu durumda ilk listede yalnızca 1 yer alır, ikincisi yoktur.
Jon Skeet

74

Bunun gibi bir şey yapabilirsiniz:

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

bana bir foreach döngüsü kurtardı.
alice7

12
Bu "l.someproperty" den birinin "p.someproperty" olması gerekmez mi?
Manos Dilaverakis

6
Bu, customlist.Where (p => otherlist.Any (l => p.someproperty! = L.someproperty)) ile basitleştirilebilir;
Dhanuka777

@ Dhanuka777 bu yanlış. Bu şekilde yapmak istiyorsanız, Anybir olmalıdır All. Bunun nedeni, ikinci listenin birinci listedeki öğeyi alabilmesidir, ancak ilk kontrol edilmemişse, o zaman hemen doğru olacaktır p.someproperty != l.someproperty. Bu, her iki listede de bulunan öğelerin iade edilmesiyle sonuçlanır. Bunu destekleyen 6 kişiye yazık.
Murphybro2

26

Vurgulamanın önemli olduğunu düşünüyorum - Hariç yöntemini kullanmak size yalnızca ikincideki öğeler olmadan birinci olan öğeleri döndürür. İlkinde görünmeyen öğeleri ikinci sırada döndürmez.

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

Ancak iki liste arasında gerçek bir fark olmasını istiyorsanız:

İkincideki maddeler olmadan birinci olan maddeler ve ikincideki maddeler olmayan maddeler.

İki kez Hariç kullanmanız gerekir:

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

Veya HashSet'in SymmetricExceptWith yöntemini kullanabilirsiniz . Ancak aranan seti değiştirir:

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

Bu yardımcı olur. Sadece OP'nin tam bir fark istemediklerini açıkça belirttiğini unutmayın: "Bu iki liste arasındaki farkı geri almam gerekiyor ( ikincideki öğeler olmadan ilkinde bulunan öğeler )".
rsenna

10
var third = first.Except(second);

( Tembel koleksiyonlara atıfta bulunmaktan hoşlanmıyorsanız, daha ToList()sonra da arayabilirsiniz Except().)

Except()Değerleri gibi, temel veri türleri olan, karşılaştırılan, bu yöntem, varsayılan karşılaştırıcısı kullanarak değerleri karşılaştırır int, string, decimalvs.

Aksi takdirde karşılaştırma Eğer Bu durumda ... istediğiniz özel nesneler uygulamak ne yapmak değil muhtemelen nesne adresi tarafından yapılacaktır IComparable(veya özel hayata IEqualityComparerve onu geçmek Except()yöntemle).


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

Not: list3Her iki listede bulunmayan öğeleri veya nesneleri içerecektir. Not: ToList()DeğiltoList()


1

Except uzantı yöntemi iki IEumerables üzerinde çalıştığından, bana bir O (n ^ 2) işlemi olacak gibi görünüyor. Performans bir sorunsa (listelerinizin büyük olduğunu söylerseniz), list1'den bir HashSet oluşturmanızı ve HashSet'in ExceptWith yöntemini kullanmanızı öneririm.


9
Enumerable.Exceptdahili kullanım HashSetveya benzeri bir şey. Kesinlikle saf O (n ^ 2) algoritmasını kullanmaz.
Jim Mischel

@Jim Mischel: Haklısınız, Enumerable.Except dahili bir Set veri yapısı kullanır ve her iki IEnumerables'dan Set'e öğeler ekler. Belgelerin bunun hakkında bir şeyler söylemesini isterdim.
foson

1

İşte benim çözümüm:

    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

biraz geç ama işte benim için çalışan bir çözüm

 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

Yukarıdaki belirtilen kodda, temel sınıfım ile türetilmiş sınıf listesi arasındaki özellik farkını alıyorum


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

Not: Sonuç Listesi, kontrol listesinde bulunan ancak
myList'te

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

Cevap kabul edildikten 6 yıl sonra ve yeni bir değer
Mitch Wheat

-3

Her iki listeniz de IEnumerable arabirimini uyguluyorsa, bunu LINQ kullanarak elde edebilirsiniz.

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

6
Ve listelerinizin her biri bir milyon öğe içeriyorsa, uzun süre bekleyeceksiniz. IEnumerable.ExceptBunun yerine kullanın .
Jim Mischel

3
Evet. Sadece yaklaşık 450 parçam var ve hala bekliyorum. Bunu kullanırken dikkatli olun.
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);
            }
        }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.