Чи має .NET спосіб перевірити, чи Список a містить усі елементи у Списку b?


98

У мене є такий спосіб:

namespace ListHelper
{
    public class ListHelper<T>
    {
        public static bool ContainsAllItems(List<T> a, List<T> b)
        {
            return b.TrueForAll(delegate(T t)
            {
                return a.Contains(t);
            });
        }
    }
}

Мета якого - визначити, чи містить Перелік усі елементи іншого списку. Мені здається, що щось подібне вже буде вбудовано у .NET, чи так це, і я дублюю функціональність?

Редагувати: Прошу вибачення за те, що не заявляю заздалегідь, що я використовую цей код у версії Mono 2.4.2.



Ваш алгоритм є квадратичним O (нм). Якщо списки відсортовані, тестування, чи є один підмножиною іншого, має бути можливим за час O (n + m).
Colonel Panic

Відповіді:


176

Якщо ви використовуєте .NET 3.5, це просто:

public class ListHelper<T>
{
    public static bool ContainsAllItems(List<T> a, List<T> b)
    {
        return !b.Except(a).Any();
    }
}

Це перевіряє, чи є елементи, в bяких немає, a- а потім інвертує результат.

Зверніть увагу, що було б дещо більш звичним зробити метод загальним, а не класом, і немає жодних підстав вимагати List<T>замість цього IEnumerable<T>- тому, мабуть, було б кращим:

public static class LinqExtras // Or whatever
{
    public static bool ContainsAllItems<T>(this IEnumerable<T> a, IEnumerable<T> b)
    {
        return !b.Except(a).Any();
    }
}

1
Це не перевірено, але не поверне b.Except (a) .Empty (); бути набагато читальнішим?
Нільс,

7
За винятком того, що Empty () не повертає логічне значення. Він повертає IEnumerable <T> без елементів.
Пітер Стівенс,

2
Я вважаю, що ви можете використовувати LINQ для об’єктів у Mono ... але було б корисно, якщо б ви вказали вимоги у питанні для початку. Яку версію Mono ви використовуєте?
Джон Скіт,

1
Якщо списки мають довжину n та m, яка складність у часі цього алгоритму?
Colonel Panic

1
@ColonelPanic: Припускаючи відсутність колізійних зіткнень, O (n + m).
Jon Skeet


35

Просто для задоволення, @ JonSkeet в відповідь як метод розширення:

/// <summary>
/// Does a list contain all values of another list?
/// </summary>
/// <remarks>Needs .NET 3.5 or greater.  Source:  https://stackoverflow.com/a/1520664/1037948 </remarks>
/// <typeparam name="T">list value type</typeparam>
/// <param name="containingList">the larger list we're checking in</param>
/// <param name="lookupList">the list to look for in the containing list</param>
/// <returns>true if it has everything</returns>
public static bool ContainsAll<T>(this IEnumerable<T> containingList, IEnumerable<T> lookupList) {
    return ! lookupList.Except(containingList).Any();
}

2
так само: Містить Any = public static bool ContainsAny<T>(this IEnumerable<T> haystack, IEnumerable<T> needle) { return haystack.Intersect(needle).Count() > 0; }. Я спробував кілька швидких порівнянь продуктивності haystack.Count() - 1 >= haystack.Except(needle).Count();та, Intersectздавалося, більшу частину часу робив краще.
drzaus

4
sheesh ... use Any()not Count() > 0: public static bool ContainsAny<T>(this IEnumerable<T> haystack, IEnumerable<T> needle) { return haystack.Intersect(needle).Any(); }
drzaus

0

Ви також можете використати інший спосіб. Замінити дорівнює і використовуйте це

public bool ContainsAll(List<T> a,List<T> check)
{
   list l = new List<T>(check);
   foreach(T _t in a)
   {
      if(check.Contains(t))
      {
         check.Remove(t);
         if(check.Count == 0)
         {
            return true;
         }
      }
      return false;
   }
}

2
list l = new List<T>(check);Я не думаю, що це буде скомпільовано, і якщо це checkбуде зроблено , це абсолютно непотрібно, як це вже перелік
Рохіт Віпін Метьюз
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.