Перевірте, чи містить один IEnumerable всі елементи іншого IEnumerable


102

Який найшвидший спосіб визначити, чи містить один IEnumerable всі елементи іншого IEnumerable при порівнянні поля / властивості кожного елемента в обох колекціях?


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

1
В який бік ваші списки? Ви хочете перевірити, чи всі елементи в списку1 є у списку 2 чи всі елементи в списку2 є у списку 1?
Марк Байєрс

Відповіді:


138

Немає «швидкого способу» зробити це, якщо ви не відстежуєте та підтримуєте деякий стан, який визначає, чи містяться всі значення однієї колекції в іншій. Якщо вам доведеться IEnumerable<T>працювати лише проти, я б скористався Intersect.

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

Виконання цього має бути дуже розумним, оскільки Intersect()перераховуватиметься над кожним списком лише один раз. Крім того, другий виклик до Count()буде оптимальним, якщо базовий тип є ICollection<T>швидше, ніж просто IEnumerable<T>.


Я робив кілька тестів, і цей метод, здається, працює швидше, ніж інші. Дякую за пораду.
Брендон Захарі

2
Це не працює, якщо в списку є дублікати. Наприклад, порівняння масиву знаків 441 та 414 повертає 41, і таким чином підрахунок не вдається.
Іван

69

Ви також можете використати Крім вилучення з першого списку всіх значень, що існують у другому списку, а потім перевірити, чи були видалені всі значення:

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

Цей метод мав перевагу не вимагати двох дзвінків до Count ().


Це також добре для з’ясування того, що є у List1, а не у List2;
Гомер

16
Це працює в ситуаціях, коли list1 має дублювані значення. Прийнята відповідь не відповідає.
dbc

23

C # 3.5+

Використовуючи Enumerable.All<TSource>для визначення, чи всі елементи List2 містяться в List1:

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

Це також буде працювати, коли list1 містить навіть більше, ніж усі елементи списку2.


10
Наслідки щодо виконання Contains()дзвінка під час All()дзвінка.
Кент Богаарт

Також ви можете перемістити його до групового методу: bool hasAll = list2Uris.All (list1Uris.Contains);
jimpanzer

У випадку IE численних типів <T> це рішення забезпечить n * m продуктивність.
Дмитро Докшин

5
Стенограма: bool hasAll = list2Uris.All(list1Uris.Contains);
Освітлювач

3

Відповідь Кента тонка і коротка, але рішення, яке він пропонує, завжди вимагає ітерації усього першого колекції. Ось вихідний код:

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

Це не завжди потрібно. Отже, ось моє рішення:

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

Насправді слід подумати про використання ISet<T>( HashSet<T>). Він містить усі необхідні методи набору. IsSubsetOfу вашому випадку.


2

оператор Linq SequenceEqual також буде працювати (але чутливий до того, що елементи переліку перебувають в одному порядку)

return list1Uris.SequenceEqual(list2Uris);

2

Рішення, позначене як відповідь, буде невдалим у разі повторень. Якщо ваш IEnumerable містить лише окремі значення, то це буде передано.

Нижче наведена відповідь для 2 списків з повторами:

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

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

Це не є гарним рішенням, оскільки він видаляє всі дублікати і фактично не порівнює їх.
Іван

2

Ви повинні використовувати HashSet замість Array.

Приклад:

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

Довідково

Єдине обмеження HasSet полягає в тому, що ми не можемо отримати елемент за індексом, як "Список", а також отримати елемент за Ключовими словниками. Все, що ви можете зробити, це перерахувати їх (для кожного, час і т.д.)

Будь ласка, дайте мені знати, чи це працює для вас


-2

Ви можете використовувати цей метод для порівняння двох списків

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

Практично той же відповідь була дана 3 роки тому: stackoverflow.com/a/16967827/5282087
Dragomok
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.