Archive

Posts Tagged ‘Contains’

LINQ: Está contido em outra coleção?

Pessoal,

Tive uma necessidade imensa de verificar se estavam contidos alguns elementos de uma coleção em outra coleção, e isso utilizando LINQ com tipos complexos. Quem conhece o LINQ sabe que não dá para comparar dois tipos complexos, logo temos q comparar os tipos primários.

Vou apresentar nesse post duas formas fáceis de comparar se os elementos de uma coleção estão contidos em outra coleção de tipos complexos.

Meu cenário é o seguinte:

Tenho uma classe Produto:

class Produto {
    public int ProdutoID { getset; }
    public string Descricao { getset; }
    public int Quantidade { getset; }

    public Produto(int produtoID, string descricao, int quantidade)
    {
        ProdutoID = produtoID;
        Descricao = descricao;
        Quantidade = quantidade;
    }
}

Dessa classe tenho duas listas:

var produtos = new []{ 
    new Produto(1"produto 1"2),
    new Produto(2"produto 2"16),
    new Produto(3"produto 3"11),
    new Produto(4"produto 4"5),
    new Produto(5"produto 5"22)};
var produtos2 = new []{ 
    new Produto(1"produto 1"2),
    new Produto(2"produto 2"16),
    new Produto(5"produto 5"22)};

Eu quero eu verificar se os elementos da coleção produtos2 estão contidos na coleção produtos. E eu não posso simplesmente inserir no LINQ o método Contains e comparar as duas instâncias, como apresentado abaixo:

var result = (from p1 in produtos where produtos2.Contains(p1) select p1).ToArray();

Vai me retornar nada, devido a identidade de cada objeto. Visto a necessidade de comparar as propriedades entre as coleções, busquei e busquei formas de fazer isso e não achei, foi ai então que fiz minha própria solução, na verdade duas.. rsrs.

Criei uma classe que implementa a interface IEqualityComparer para usar no método Contains e criei meu próprio ContainsByProperty.

A classe com a implementação do IEqualityComparer, ficou da seguinte forma:

class PropertyEqualityComparer<TSource> : IEqualityComparer<TSource>
{
    Func<TSource, object> _keySelector;

    public PropertyEqualityComparer(Func<TSource, object> keySelector)
    {
        _keySelector = keySelector;
    }

    public bool Equals(TSource x, TSource y)
    {
        return _keySelector(x).Equals(_keySelector(y));
    }

    public int GetHashCode(TSource obj)
    {
        return obj.GetHashCode();
    }
}

Podendo ser usado da seguinte forma:

var result = (from p1 in produtos
      where produtos2.Contains(p1, 
           new PropertyEqualityComparer<Produto>(p => p.ProdutoID)) select p1).ToArray();

E o método ContainsByProperty (que eu preferi), ficou da seguinte forma:

public static bool ContainsByProperty<TSource, TKey>(this IEnumerable<TSource> source, TSource value, Func<TSource, TKey> keySelector) 
{
    foreach (TSource item in source)
       if (keySelector(value).Equals(keySelector(item)))
           return true;
    return false;
}

Podendo ser usado da seguinte forma:

var result2 = (from p1 in produtos
               where produtos2.ContainsByProperty(p1, p => p.ProdutoID)
               select p1).ToArray();

A referencia que mais me ajudou a implementar esses métodos foi o http://stackoverflow.com/questions/1082624/isorderedby-extension-method

Espero que esses códigos ajudem vocês assim como me ajudou e muito. Qualquer coisa pode comentar ou mande um e-mail, estarei à disposição.

Abraços