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.