O .NET tem uma maneira de verificar se a Lista a contém todos os itens da Lista b?

97

Eu tenho o seguinte método:

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

O objetivo é determinar se uma lista contém todos os elementos de outra lista. Parece-me que algo assim já estaria integrado no .NET, é esse o caso e estou duplicando a funcionalidade?

Edit: Minhas desculpas por não declarar antecipadamente que estou usando este código no Mono versão 2.4.2.

Matt Haley
fonte
Consulte também stackoverflow.com/questions/332973/…
Coronel Panic
Seu algoritmo é quadrático O (nm). Se as listas forem classificadas, testar se uma é um subconjunto de outra deve ser possível em tempo O (n + m).
Coronel Panic

Respostas:

175

Se você estiver usando .NET 3.5, é fácil:

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

Isso verifica se há elementos nos bquais não estão a- e então inverte o resultado.

Observe que seria um pouco mais convencional tornar o método genérico em vez da classe, e não há razão para exigir em List<T>vez de IEnumerable<T>- então provavelmente seria preferível:

public static class LinqExtras // Or whatever
{
    public static bool ContainsAllItems<T>(this IEnumerable<T> a, IEnumerable<T> b)
    {
        return !b.Except(a).Any();
    }
}
Jon Skeet
fonte
1
Isso não foi testado, mas não retornaria b.Except (a) .Empty (); ser muito mais legível?
Nils
7
Exceto que Empty () não retorna um booleano. Ele retorna um IEnumerable <T> sem itens.
Peter Stephens,
2
Você pode usar LINQ to Objects no Mono, eu acredito ... mas seria útil se você declarasse os requisitos na pergunta para começar. Qual versão do Mono você está usando?
Jon Skeet
1
Se as listas têm comprimento n e m, qual é a complexidade de tempo desse algoritmo?
Coronel Panic
1
@ColonelPanic: Supondo que não haja colisões de hash, O (n + m).
Jon Skeet
37

Incluído no .NET 4: Enumerable.All

public static bool ContainsAll<T>(IEnumerable<T> source, IEnumerable<T> values)
{
    return values.All(value => source.Contains(value));
}
Thomas
fonte
35

Apenas por diversão, a resposta de @JonSkeet como um método de extensão:

/// <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();
}
drzaus
fonte
2
da mesma forma: Contains Any = public static bool ContainsAny<T>(this IEnumerable<T> haystack, IEnumerable<T> needle) { return haystack.Intersect(needle).Count() > 0; }. Eu tentei algumas comparações rápidas de desempenho haystack.Count() - 1 >= haystack.Except(needle).Count();e Intersectparecia me sair melhor na maioria das vezes.
drzaus
4
sheesh ... Any()não use Count() > 0: public static bool ContainsAny<T>(this IEnumerable<T> haystack, IEnumerable<T> needle) { return haystack.Intersect(needle).Any(); }
drzaus
0

Você também pode usar outra maneira. Substituir igual e usar este

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;
   }
}
user3210251
fonte
2
list l = new List<T>(check);Eu não acho que isso iria compilar e se o fizer, é totalmente desnecessário, pois checkjá é uma lista
Rohit Vipin Mathews