Periksa apakah satu IEnumerable berisi semua elemen IEnumerable lainnya

102

Apa cara tercepat untuk menentukan apakah satu IEnumerable berisi semua elemen IEnumerable lain saat membandingkan bidang / properti dari setiap elemen di kedua koleksi?


public class Item
{
    public string Value;

    public Item(string value)
    {
        Value = value;
    }
}

//example usage

Item[] List1 = {new Item("1"),new Item("a")};
Item[] List2 = {new Item("a"),new Item("b"),new Item("c"),new Item("1")};

bool Contains(IEnumerable<Item> list1, IEnumerable<Item>, list2)
{
    var list1Values = list1.Select(item => item.Value);
    var list2Values = list2.Select(item => item.Value);

    return //are ALL of list1Values in list2Values?
}

Contains(List1,List2) // should return true
Contains(List2,List1) // should return false
Brandon Zacharie
sumber
1
Bagaimana daftar Anda? Apakah Anda ingin memeriksa apakah semua item di list1 ada di list 2 atau semua item di list2 ada di daftar 1?
Mark Byers

Jawaban:

138

Tidak ada "cara cepat" untuk melakukan ini kecuali Anda melacak dan mempertahankan beberapa status yang menentukan apakah semua nilai dalam satu koleksi dimuat di koleksi lain. Jika Anda hanya harus IEnumerable<T>melawan, saya akan menggunakan Intersect.

var allOfList1IsInList2 = list1.Intersect(list2).Count() == list1.Count();

Performa ini harus sangat masuk akal, karena Intersect()akan menghitung setiap daftar hanya sekali. Selain itu, panggilan kedua ke Count()akan optimal jika tipe yang mendasarinya ICollection<T>bukan hanya sebuah IEnumerable<T>.

Kent Boogaart
sumber
Saya melakukan beberapa tes dan metode ini tampaknya berjalan lebih cepat daripada yang lain. Terima kasih atas tipnya.
Brandon Zacharie
2
Ini tidak berfungsi jika ada duplikat dalam daftar. Misalnya membandingkan array karakter 441 dan 414 menghasilkan 41 dan dengan demikian penghitungan gagal.
Yohanes
69

Anda juga bisa menggunakan Kecuali untuk menghapus dari daftar pertama semua nilai yang ada di daftar kedua, dan kemudian memeriksa apakah semua nilai telah dihapus:

var allOfList1IsInList2 = !list1.Except(list2).Any();

Metode ini memiliki keuntungan karena tidak memerlukan dua panggilan ke Count ().

JW
sumber
Ini juga bagus untuk mencari tahu apa yang ada di List1 tapi tidak di List2;
Homer
16
Ini berfungsi dalam situasi di mana list1 memiliki nilai duplikat. Jawaban yang diterima tidak.
dbc
23

C # 3.5+

Menggunakan Enumerable.All<TSource>untuk menentukan apakah semua item List2 terkandung dalam List1:

bool hasAll = list2Uris.All(itm2 => list1Uris.Contains(itm2));

Ini juga akan bekerja ketika list1 berisi lebih dari semua item list2.

John K
sumber
10
Aduh pada implikasi kinerja dari Contains()panggilan dalam All()panggilan.
Kent Boogaart
Anda juga dapat memindahkannya ke metode grup: bool hasAll = list2Uris.All (list1Uris.Contains);
simpanser
Saya kasus jenis IEnumerable <T> solusi ini akan memberikan kinerja n * m.
Dmitriy Dokshin
5
Singkatan: bool hasAll = list2Uris.All(list1Uris.Contains);
Illuminator
3

Jawaban Kent bagus dan singkat, tetapi solusi yang dia berikan selalu membutuhkan pengulangan atas seluruh koleksi pertama. Berikut kode sumbernya:

public static IEnumerable<TSource> Intersect<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
{
    if (first == null)
        throw Error.ArgumentNull("first");
    if (second == null)
        throw Error.ArgumentNull("second");
    return Enumerable.IntersectIterator<TSource>(first, second, comparer);
}

private static IEnumerable<TSource> IntersectIterator<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
{
    Set<TSource> set = new Set<TSource>(comparer);
    foreach (TSource source in second)
        set.Add(source);
    foreach (TSource source in first)
    {
        if (set.Remove(source))
            yield return source;
    }
}

Itu tidak selalu dibutuhkan. Jadi, inilah solusi saya:

public static bool Contains<T>(this IEnumerable<T> source, IEnumerable<T> subset, IEqualityComparer<T> comparer)
{
    var hashSet = new HashSet<T>(subset, comparer);
    if (hashSet.Count == 0)
    {
        return true;
    }

    foreach (var item in source)
    {
        hashSet.Remove(item);
        if (hashSet.Count == 0)
        {
            break;
        }
    }

    return hashSet.Count == 0;
}

Sebenarnya, Anda harus berpikir tentang menggunakan ISet<T>( HashSet<T>). Ini berisi semua metode set yang diperlukan.IsSubsetOfdalam kasus Anda.

Dmitriy Dokshin
sumber
2

Operator Linq, SequenceEqual akan bekerja juga (tetapi sensitif terhadap item enumerable yang berada dalam urutan yang sama)

return list1Uris.SequenceEqual(list2Uris);
bkaid
sumber
2

Solusi yang ditandai sebagai jawaban akan gagal dalam kasus pengulangan. Jika IEnumerable Anda hanya berisi nilai yang berbeda maka itu akan berlalu.

Jawaban di bawah ini adalah untuk 2 daftar dengan pengulangan:

        int aCount = a.Distinct().Count();
        int bCount = b.Distinct().Count();

        return aCount == bCount &&
               a.Intersect(b).Count() == aCount;
Chandramouleswaran Ravichandra
sumber
Ini bukan solusi yang baik karena menghapus semua duplikat dan tidak benar-benar membandingkannya.
Yohanes
2

Anda harus menggunakan HashSet, bukan Array.

Contoh:

List1.SetEquals(List2); //returns true if the collections contains exactly same elements no matter the order they appear in the collection

Referensi

Satu-satunya batasan HasSet adalah kita tidak bisa mendapatkan item dengan indeks seperti List atau mendapatkan item dengan Key seperti Kamus. Yang dapat Anda lakukan hanyalah menghitungnya (untuk masing-masing, sementara, dll)

Tolong beri tahu saya jika itu berhasil untuk Anda

Rick
sumber
-2

Anda dapat menggunakan metode ini untuk membandingkan dua daftar

    //Method to compare two list
    private bool Contains(IEnumerable<Item> list1, IEnumerable<Item> list2)
    {
        bool result;

        //Get the value
        var list1WithValue = list1.Select(s => s.Value).ToList();
        var list2WithValue = list2.Select(s => s.Value).ToList();

        result = !list1WithValue.Except(list2WithValue).Any();

        return result;
    }
Sathish
sumber
Jawaban yang kurang lebih sama diberikan 3 tahun sebelumnya: stackoverflow.com/a/16967827/5282087
Dragomok