Certo, então eu tenho um enumerável e desejo obter valores distintos dele.
Usando System.Linq
, é claro que existe um método de extensão chamado Distinct
. No caso simples, pode ser usado sem parâmetros, como:
var distinctValues = myStringList.Distinct();
Muito bem, mas se eu tiver um grande número de objetos para os quais preciso especificar igualdade, a única sobrecarga disponível é:
var distinctValues = myCustomerList.Distinct(someEqualityComparer);
O argumento comparador de igualdade deve ser uma instância de IEqualityComparer<T>
. Eu posso fazer isso, é claro, mas é um pouco detalhado e, bem, desajeitado.
O que eu esperava era uma sobrecarga que levaria um lambda, digamos um Func <T, T, bool>:
var distinctValues
= myCustomerList.Distinct((c1, c2) => c1.CustomerId == c2.CustomerId);
Alguém sabe se existe alguma extensão ou alguma solução alternativa? Ou eu estou esquecendo de alguma coisa?
Como alternativa, existe uma maneira de especificar um inline IEqualityComparer (me envergonhar)?
Atualizar
Encontrei uma resposta de Anders Hejlsberg em uma postagem em um fórum do MSDN sobre esse assunto. Ele diz:
O problema que você encontrará é que, quando dois objetos se comparam da mesma forma, eles devem ter o mesmo valor de retorno GetHashCode (ou a tabela de hash usada internamente pelo Distinct não funcionará corretamente). Usamos IEqualityComparer porque empacota implementações compatíveis de Equals e GetHashCode em uma única interface.
Suponho que faz sentido ..
fonte
.Distinct(new KeyEqualityComparer<Customer,string>(c1 => c1.CustomerId))
, e explicar por que GetHashCode () é importante para o trabalho corretamente.Respostas:
fonte
DistinctBy
(ou até mesmoDistinct
, uma vez que a assinatura será única).yield
instrução, portanto, tecnicamente, o streaming não é possível. Obrigado pela sua resposta embora. Vou usá-lo ao codificar em C #. ;-)Parece-me que você deseja
DistinctBy
do MoreLINQ . Você pode então escrever:Aqui está uma versão detalhada de
DistinctBy
(sem verificação de nulidade e sem opção para especificar seu próprio comparador de chaves):fonte
yield
+ lib adicional, foreach pode ser re-escrita comoreturn source.Where(element => knownKeys.Add(keySelector(element)));
Para embrulhar as coisas . Eu acho que a maioria das pessoas que vieram aqui como eu deseja a solução mais simples possível sem usar nenhuma biblioteca e com o melhor desempenho possível .
(Para mim, o grupo aceito pelo método é um exagero em termos de desempenho.)
Aqui está um método de extensão simples usando a interface IEqualityComparer que também funciona para valores nulos.
Uso:
Código do método de extensão
fonte
Não existe tal sobrecarga de método de extensão para isso. Eu achei isso frustrante no passado e, como tal, costumo escrever uma classe auxiliar para lidar com esse problema. O objetivo é converter um
Func<T,T,bool>
paraIEqualityComparer<T,T>
.Exemplo
Isso permite que você escreva o seguinte
fonte
IEqualityComparer<T>
a partir de uma projeção: stackoverflow.com/questions/188120/...Solução taquigráfica
fonte
Isso fará o que você deseja, mas eu não sei sobre desempenho:
Pelo menos não é detalhado.
fonte
Aqui está um método de extensão simples que faz o que eu preciso ...
É uma pena que eles não tenham adotado um método distinto como esse no framework, mas ei.
fonte
x.Key
parax.First()
e altere o valor de retorno paraIEnumerable<T>
Algo que usei que funcionou bem para mim.
fonte
Todas as soluções que vi aqui dependem da seleção de um campo já comparável. Se for necessário comparar de uma maneira diferente, porém, esta solução aqui parece funcionar geralmente, para algo como:
fonte
Pegue de outra maneira:
A sequência retorna elementos distintos comparando-os pela propriedade '_myCaustomerProperty'.
fonte
Você pode usar o InlineComparer
Amostra de uso :
Fonte: https://stackoverflow.com/a/5969691/206730
Usando o IEqualityComparer for Union
Posso especificar meu comparador explícito de tipo explícito?
fonte
Você pode usar o LambdaEqualityComparer:
fonte
Uma maneira complicada de fazer isso é usar
Aggregate()
extension, usando um dicionário como acumulador com os valores da propriedade-chave como chaves:E uma solução no estilo GroupBy está usando
ToLookup()
:fonte
Dictionary<int, Customer>
?Suponho que você tenha um IEnumerable e, no seu exemplo de delegado, gostaria que c1 e c2 se referissem a dois elementos nesta lista?
Eu acredito que você poderia conseguir isso com uma associação automática var distinctResults = de c1 em myList junção c2 em myList em
fonte
Se
Distinct()
não produzir resultados exclusivos, tente este:fonte
O pacote Microsoft System.Interactive possui uma versão do Distinct que usa um seletor de chave lambda. É efetivamente o mesmo que a solução de Jon Skeet, mas pode ser útil que as pessoas saibam e consultem o restante da biblioteca.
fonte
Veja como você pode fazer isso:
Este método permite que você o use especificando um parâmetro como
.MyDistinct(d => d.Name)
, mas também permite especificar uma condição de ter como um segundo parâmetro como:Nota: Isso também permitirá que você especifique outras funções, como por exemplo
.LastOrDefault(...)
.Se você deseja expor apenas a condição, pode ser ainda mais simples implementando-a como:
Nesse caso, a consulta seria parecida com:
NB Aqui, a expressão é mais simples, mas a nota é
.MyDistinct2
usada.FirstOrDefault(...)
implicitamente.Nota: Os exemplos acima estão usando a seguinte classe de demonstração
fonte
IEnumerable
extensão lambda:Uso:
fonte