Soru Bir IEnumerable'ın başka bir IEnumerable öğesinin tüm öğelerini içerip içermediğini kontrol edin


Bir IEnumerable'ın, her iki koleksiyondaki her öğenin bir alanını / özelliğini karşılaştırırken başka bir IEnumerable öğesinin tümünü içerip içermediğini belirlemenin en hızlı yolu nedir?


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

71
2017-07-18 06:17


Menşei


Listeleriniz hangi yönde? Liste1'deki tüm öğelerin listede 2 olup olmadığını veya liste2'deki tüm öğelerin liste 1'de olup olmadığını kontrol etmek ister misiniz? - Mark Byers


Cevaplar:


Bir koleksiyondaki tüm değerlerin bir diğerinde bulunup bulunmadığını belirleyen bir durumu izleyip sürdürmediğiniz sürece bunu yapmak için "hızlı yol" yoktur. Sadece sahipseniz IEnumerable<T> Çalışmak için kullanırdım Intersect.

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

Bunun performansı çok makul olmalı, çünkü Intersect() her listenin bir kez numaralandıracaktır. Ayrıca, ikinci çağrı Count() altta yatan tür bir ise optimal olacaktır ICollection<T> sadece bir IEnumerable<T>.


102
2017-07-18 08:21



Bazı testler yaptım ve bu yöntem diğerlerinden daha hızlı çalışıyor gibi görünüyor. Bahşiş için teşekkürler. - Brandon Zacharie
bence demek istedin ki var allOfList2IsInList1 = list1.Intersect(list2).Count() == list2.Count(); - dan
@fsmmu: hayır, yapmadım. İlk çağrı, 1 ve 2 listelerinin kesişiminde kaç öğenin bulunduğunu bulur. İkinci çağrı, 1. öğede kaç öğenin bulunduğunu bulur. Bu sayılar aynıysa, liste 1, OP'nin başına göre list 2'de yer alır. soru. - Kent Boogaart
Ah, o zaman sadece kötü değişken ismi allOfList2IsInList1 - dan
@jsmmu: oldukça doğru - sabit. - Kent Boogaart


Ayrıca, ilk listeden ikinci listede bulunan tüm değerleri kaldırmak için Except'i kullanabilir ve ardından tüm değerlerin kaldırılıp kaldırılmadığını kontrol edebilirsiniz:

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

Bu yöntem, Count () için iki çağrı gerektirmemesi avantajına sahipti.


34
2018-06-06 16:53



Bu, List1'de ne var, ancak List2'de ne olduğunu bulmak için de iyidir; - Homer
Bu, list1'in yinelenen değerlere sahip olduğu durumlarda çalışır. Kabul edilen cevap yok. - dbc


C # 3.5+

kullanma Enumerable.All<TSource> List2'de tüm List2 öğelerinin bulunup bulunmadığını belirlemek için:

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

Bu liste1 list2'nin tüm öğelerinden daha fazlasını içerdiğinde de çalışır.


18
2017-07-18 06:50



Performans performansında bir Contains() içinde ara All() aramak. - Kent Boogaart
Ayrıca grup yöntemine taşıyabilirsiniz: bool hasAll = list2Uris.All (list1Uris.Contains); - jimpanzer
IEnumerable <T> türlerinden biri durumunda bu çözüm n * m performansı sağlayacaktır. - Dmitriy Dokshin
Steno:  bool hasAll = list2Uris.All(list1Uris.Contains); - Illuminator


Cevap olarak işaretlenen çözüm, tekrarlama durumunda başarısız olur. IEnumerable sadece farklı değerler içeriyorsa o zaman geçerdi.

Aşağıdaki cevap, tekrarlı 2 liste içindir:

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

        return aCount == bCount &&
               a.Intersect(b).Count() == aCount;

4
2018-05-19 01:12





Kent'in cevabı iyi ve kısa ama verdiği çözüm, ilk koleksiyonun tamamında yinelemeyi gerektirir. İşte kaynak kodu:

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

Bu her zaman gerekli değildir. İşte benim çözümüm:

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

Aslında, kullanmayı düşünmelisin ISet<T> (HashSet<T>). Gerekli tüm set yöntemlerini içerir. IsSubsetOf Senin durumunda.


3
2017-08-08 21:13





Linq operatörü SequenceEqual da çalışırdı (ancak aynı sırayla numaralandırılan öğelere karşı hassastır)

return list1Uris.SequenceEqual(list2Uris);

2
2017-07-18 06:28





Array yerine HashSet kullanmalısınız.

Örnek:

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

Referans

Tek HasSet sınırlaması, Liste gibi indekse göre öğe alamamamız ve Anahtar kelimeler gibi Anahtar kelimeler almamamızdır. Tek yapmanız gereken onları sıralamaktır (her biri için, vb.)

Bu sizin için çalışıyorsa lütfen bana bildirin


0
2018-02-07 21:01