Perbedaan antara dua daftar


118

Saya Memiliki dua daftar umum yang diisi dengan CustomsObjects.

Saya perlu mendapatkan kembali perbedaan antara kedua daftar tersebut (Item yang ada di pertama tanpa item di yang kedua) di yang ketiga.

Saya pikir menggunakan .Except()itu ide yang bagus tapi saya tidak mengerti bagaimana menggunakan ini .. Tolong!

c# 

Jawaban:


237

Menggunakan Exceptadalah cara yang tepat untuk pergi. Jika jenis Anda menimpa Equalsdan GetHashCode, atau Anda hanya tertarik pada persamaan jenis referensi (yaitu dua referensi hanya "sama" jika merujuk ke objek yang sama persis), Anda dapat menggunakan:

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

Jika Anda perlu mengungkapkan ide kustom tentang kesetaraan, misalnya dengan ID, Anda harus menerapkan IEqualityComparer<T>. Sebagai contoh:

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

Kemudian gunakan:

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

Perhatikan bahwa ini akan menghapus elemen duplikat. Jika Anda membutuhkan duplikat untuk dipertahankan, mungkin akan lebih mudah untuk membuat satu set list2dan menggunakan sesuatu seperti:

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

18
Sekadar tambahan, saya akan menambahkan itu Exceptadalah operasi Set , maka daftar yang dihasilkan akan memiliki nilai yang berbeda, misalnya {'A','A','B','C'}.Except({'B','C'}) return{'A'}
digEmAll

4
Saya punya ini: {'A', 'A', 'B', 'C'}. Kecuali ({'B', 'C'}) mengembalikan {'A'} yang berfungsi ... namun {'B' , 'C'}. Kecuali ({'A', 'B', 'C'}) DOES NOT return {'A'}
MrLister

2
Perbedaan himpunan dua himpunan didefinisikan sebagai anggota himpunan pertama yang tidak muncul pada himpunan kedua (mengutip MS). Jadi, {B, C} .Except ({A, B, C}) seharusnya tidak menghasilkan apa-apa karena B dan C keduanya di set kedua. Bukan bug, lebih berkaitan dengan definisi matematika dari fungsi tersebut.
Steve Hibbert

Jadi @ JonSkeet jika saya ingin membandingkan dua daftar berdasarkan katakanlah 2 properti saya dapat menulis pembanding dengan cara ini dan mendapatkan item yang tidak sama bukan?
Ehsan Sajjad

1
@NetMage: OP menyatakan mereka menginginkan "Item yang berada di pertama tanpa item di yang kedua" - kedengarannya seperti perbedaan yang ditetapkan bagi saya. Jika daftar pertama berisi {5, 5, 5, 5, 1} dan daftar kedua berisi {5} maka hanya 1 yang ada di daftar pertama tetapi bukan yang kedua.
Jon Skeet

74

Anda bisa melakukan sesuatu seperti ini:

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

itu menyelamatkan saya satu foreach loop.
alice7

12
Tidakkah seharusnya salah satu dari "hak milik" itu menjadi "hak milik"?
Manos Dilaverakis

6
Ini dapat disederhanakan dengan customlist.Where (p => otherlist.Any (l => p.someproperty! = L.someproperty));
Dhanuka777

@ Dhanuka777 itu salah. Ini Anyharus menjadi Alljika Anda ingin melakukannya seperti itu. Ini karena daftar kedua dapat memiliki item dari daftar pertama, tetapi jika bukan yang pertama dicentang maka itu akan segera mendapatkan kebenaran untuk p.someproperty != l.someproperty. Ini menghasilkan item yang dikembalikan yang ada di kedua daftar. Malu pada 6 orang yang memberi suara positif ini.
Murphybro2

26

Saya pikir penting untuk ditekankan - menggunakan metode Kecuali akan mengembalikan Anda item yang berada di pertama tanpa item di yang kedua saja. Itu tidak mengembalikan elemen-elemen di detik yang tidak muncul di pertama.

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

Tetapi jika Anda ingin mendapatkan perbedaan nyata antara dua daftar:

Item yang ada di pertama tanpa item di yang kedua dan item yang berada di urutan kedua tanpa item di yang pertama.

Anda perlu menggunakan Kecuali dua kali:

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

Atau Anda dapat menggunakan metode SymmetricExceptWith dari HashSet. Tapi itu mengubah set yang disebut:

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

Itu sangat membantu. Sadarilah OP telah menjelaskan bahwa mereka tidak menginginkan perbedaan penuh: "Saya perlu mengambil perbedaan antara kedua daftar tersebut ( Item yang ada di pertama tanpa item di daftar kedua )".
rsenna

10
var third = first.Except(second);

(Anda juga dapat menelepon ToList()setelahnya Except(), jika Anda tidak suka mereferensikan koleksi malas.)

The Except()metode membandingkan nilai-nilai menggunakan comparer default, jika nilai-nilai yang dibandingkan adalah jenis basis data, seperti int, string, decimaldll

Jika tidak, perbandingan akan dibuat berdasarkan alamat objek, yang mungkin bukan yang Anda inginkan ... Dalam hal ini, buat objek kustom Anda diimplementasikan IComparable(atau implementasikan kustom IEqualityComparerdan teruskan ke Except()metode).


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

Catatan: list3akan berisi item atau objek yang tidak ada di kedua daftar. Catatan: Ini ToList()tidaktoList()


1

Karena metode ekstensi Kecuali beroperasi pada dua IEumerables, menurut saya itu akan menjadi operasi O (n ^ 2). Jika kinerja menjadi masalah (jika mengatakan daftar Anda besar), saya sarankan untuk membuat HashSet dari list1 dan menggunakan metode KecualiWith HashSet.


9
Enumerable.Exceptpenggunaan internal HashSetatau yang serupa. Ini pasti tidak menggunakan algoritma O (n ^ 2) naif.
Jim Mischel

@ Jim Mischel: Anda benar, Enumerable. Kecuali menggunakan struktur data Set internal dan menambahkan item dari kedua IEnumerables ke dalam Set. Berharap dokumentasinya mengatakan sesuatu tentang itu.
foson

1

inilah solusi saya:

    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

agak terlambat tetapi di sini adalah solusi yang berfungsi untuk saya

 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

Dalam kode yang disebutkan di atas saya mendapatkan perbedaan properti antara kelas dasar saya dan daftar kelas turunan


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

Catatan: Daftar Hasil adalah item yang ada di daftar periksa tetapi tidak di 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 tahun setelah jawaban diterima dan tidak menambahkan nilai baru
Mitch Wheat

-3

Jika kedua daftar Anda menerapkan antarmuka IEnumerable, Anda dapat mencapai ini menggunakan LINQ.

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

6
Dan jika daftar Anda masing-masing berisi satu juta item, Anda akan menunggu lama sekali. Gunakan IEnumerable.Exceptsebagai gantinya.
Jim Mischel

3
Ya. Saya hanya punya sekitar 450 item dan saya masih menunggu. Hati-hati menggunakan ini.
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);
            }
        }
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.