Verifique se um IEnumerable contém todos os elementos de outro IEnumerable


102

Qual é a maneira mais rápida de determinar se um IEnumerable contém todos os elementos de outro IEnumerable ao comparar um campo / propriedade de cada elemento em ambas as coleções?


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
Em que sentido estão suas listas? Deseja verificar se todos os itens na lista1 estão na lista 2 ou se todos os itens na lista2 estão na lista 1?
— Mark Byers

Respostas:


138

Não existe uma "maneira rápida" de fazer isso, a menos que você rastreie e mantenha algum estado que determine se todos os valores em uma coleção estão contidos em outra. Se você só tiver IEnumerable<T>que trabalhar contra, eu usaria Intersect.

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

O desempenho deste deve ser muito razoável, já Intersect()que enumerará sobre cada lista apenas uma vez. Além disso, a segunda chamada para Count()será ideal se o tipo subjacente for um em ICollection<T>vez de apenas um IEnumerable<T>.


Fiz alguns testes e esse método parece funcionar mais rápido do que os outros. Obrigado pela dica.
— Brandon Zacharie

2
Isso não funciona se houver duplicatas na lista. Por exemplo, comparar uma matriz char de 441 e 414 retorna 41 e, portanto, a contagem falha.
— John

69

Você também pode usar Except para remover da primeira lista todos os valores que existem na segunda lista e, em seguida, verificar se todos os valores foram removidos:

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

Esse método tinha a vantagem de não exigir duas chamadas para Count ().


Isso também é bom para descobrir o que está na Lista1, mas não na Lista2;
— Homer de

16
Isso funciona em situações em que lista1 tem valores duplicados. A resposta aceita não.
— dbc

23

C # 3.5+

Usando Enumerable.All<TSource>para determinar se todos os itens da Lista2 estão contidos na Lista1:

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

Isso também funcionará quando a lista1 contiver ainda mais do que todos os itens da lista2.


10
Ouch em implicações de desempenho de uma Contains()chamada dentro de uma All()chamada.
— Kent Boogaart

Você também pode movê-lo para o método de grupo: bool hasAll = list2Uris.All (list1Uris.Contains);
— Jimpanzer

No caso dos tipos IEnumerable <T>, esta solução fornecerá desempenho n * m.
— Dmitriy Dokshin

4
Abreviação: bool hasAll = list2Uris.All(list1Uris.Contains);
— Iluminador de

3

A resposta de Kent é boa e curta, mas a solução que ele fornece sempre requer iteração em toda a primeira coleção. Aqui está o código-fonte:

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

Isso nem sempre é necessário. Então, aqui está minha solução:

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

Na verdade, você deve pensar em usar ISet<T>( HashSet<T>). Ele contém todos os métodos de definição necessários. IsSubsetOfno seu caso.


2

o operador Linq SequenceEqual também funcionaria (mas é sensível aos itens do enumerável estarem na mesma ordem)

return list1Uris.SequenceEqual(list2Uris);

2

A solução marcada como resposta falhará no caso de repetições. Se seu IEnumerable contiver apenas valores distintos, ele será aprovado.

A resposta abaixo é para 2 listas com repetições:

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

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

Esta não é uma boa solução, já que remove todas as duplicatas e não as compara.
— John

2

Você deve usar HashSet em vez de Array.

Exemplo:

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

Referência

A única limitação do HasSet é que não podemos obter item por índice como Lista, nem obter item por Chave como Dicionários. Tudo que você pode fazer é enumerá-los (para cada um, enquanto, etc.)

Por favor, deixe-me saber se isso funciona para você


-2

você pode usar este método para comparar duas listas

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

Praticamente a mesma resposta foi dada 3 anos antes: stackoverflow.com/a/16967827/5282087
— Dragomok
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.