Estou brincando com o LINQ para aprender sobre isso, mas não consigo descobrir como usá-lo Distinct
quando não tenho uma lista simples (é fácil fazer uma lista simples de números inteiros, essa não é a questão). O que eu quero usar Distinct em uma lista de um Objeto em uma ou mais propriedades do objeto?
Exemplo: se um objeto estiver Person
, com Propriedade Id
. Como posso obter toda pessoa e usá Distinct
-la com a propriedade Id
do objeto?
Person1: Id=1, Name="Test1"
Person2: Id=1, Name="Test1"
Person3: Id=2, Name="Test2"
Como posso obter apenas Person1
e Person3
? Isso é possível?
Se não for possível com o LINQ, qual seria a melhor maneira de ter uma lista Person
dependendo de algumas de suas propriedades no .NET 3.5?
GroupBy
mais fácil usar . Se você precisar em mais de um lugar, é muito mais limpo (IMO) encapsular a intenção.IQueryable<T>
aqui, não vejo como isso é relevante. Concordo que isso não seria adequado para EF etc, mas dentro do LINQ to Objects acho que é mais adequado do queGroupBy
. O contexto da pergunta é sempre importante.Simples! Você deseja agrupá-los e escolher um vencedor do grupo.
Se você deseja definir grupos em várias propriedades, veja como:
fonte
Single()
eSingleOrDefault()
cada um são lançados quando a fonte possui mais de um item. Nesta operação, esperamos a possibilidade de cada grupo ter mais de um item. Nesse caso,First()
é preferívelFirstOrDefault()
que cada grupo deva ter pelo menos um membro ... a menos que você esteja usando EntityFramework, que não pode descobrir que cada grupo tem pelo menos um membro e exigeFirstOrDefault()
.FirstOrDefault()
github.com/dotnet/efcore/issues/12088 . Estou no 3.1 e recebo erros "incapazes de traduzir".Usar:
Isso
where
ajuda a filtrar as entradas (poderia ser mais complexa)groupby
eselect
executar a função distinta.fonte
Você também pode usar a sintaxe da consulta se quiser que ela seja semelhante a LINQ:
fonte
Eu acho que é o suficiente:
fonte
Solução primeiro agrupe por seus campos e selecione o primeiro item de falha padrão.
fonte
Você pode fazer isso com o padrão
Linq.ToLookup()
. Isso criará uma coleção de valores para cada chave exclusiva. Basta selecionar o primeiro item da coleçãofonte
O código a seguir é funcionalmente equivalente à resposta de Jon Skeet .
Testado no .NET 4.5, deve funcionar em qualquer versão anterior do LINQ.
Incidencialmente, confira a versão mais recente do Jon Skeet do DistinctBy.cs no Google Code .
fonte
Escrevi um artigo que explica como estender a função Distinct para que você possa fazer o seguinte:
Aqui está o artigo: Estendendo LINQ - Especificando uma Propriedade na Função Distinta
fonte
Pessoalmente, uso a seguinte classe:
Em seguida, um método de extensão:
Finalmente, o uso pretendido:
A vantagem que encontrei usando essa abordagem é a reutilização da
LambdaEqualityComparer
classe para outros métodos que aceitam umIEqualityComparer
. (Ah, e deixo oyield
material para a implementação original do LINQ ...)fonte
Caso você precise de um método Distinct em várias propriedades, confira minhas PowerfulExtensions biblioteca . Atualmente, ele está em uma fase muito jovem, mas já é possível usar métodos como Distinct, Union, Intersect, Exceto em qualquer número de propriedades;
É assim que você o usa:
fonte
Quando enfrentamos essa tarefa em nosso projeto, definimos uma pequena API para compor comparadores.
Portanto, o caso de uso era assim:
E a própria API se parece com isso:
Mais detalhes estão no nosso site: IEqualityComparer no LINQ .
fonte
Você pode usar DistinctBy () para obter registros Distinct por uma propriedade de objeto. Basta adicionar a seguinte declaração antes de usá-la:
e use-o da seguinte maneira:
onde 'Index' é a propriedade na qual eu quero que os dados sejam distintos.
fonte
Você pode fazer isso (embora não seja rápido demais) assim:
Ou seja, "selecione todas as pessoas onde não há outra pessoa diferente na lista com o mesmo ID".
Lembre-se, no seu exemplo, que selecionaria a pessoa 3. Não tenho certeza de como saber o que você deseja, dentre as duas anteriores.
fonte
Se você não quiser adicionar a biblioteca MoreLinq ao seu projeto apenas para obter a
DistinctBy
funcionalidade, poderá obter o mesmo resultado final usando a sobrecarga doDistinct
método Linq que leva umIEqualityComparer
argumento.Você começa criando uma classe comparadora de igualdade customizada genérica que usa a sintaxe lambda para executar a comparação customizada de duas instâncias de uma classe genérica:
Então, no seu código principal, você o usa da seguinte maneira:
Voila! :)
O acima pressupõe o seguinte:
Person.Id
é do tipoint
people
coleção não contém nenhum elemento nuloSe a coleção puder conter nulos, basta reescrever as lambdas para verificar se há nulos, por exemplo:
EDITAR
Essa abordagem é semelhante à da resposta de Vladimir Nesterovsky, mas mais simples.
Também é semelhante ao da resposta de Joel, mas permite uma lógica de comparação complexa envolvendo várias propriedades.
No entanto, se seus objetos só puderem diferir,
Id
então outro usuário deu a resposta correta de que tudo que você precisa fazer é substituir as implementações padrão deGetHashCode()
eEquals()
na suaPerson
classe e, em seguida, basta usar oDistinct()
método pronto para uso do Linq para filtrar quaisquer duplicatas.fonte
A melhor maneira de fazer isso que seja compatível com outras versões do .NET é substituir Equals e GetHash para lidar com isso (consulte Pergunta sobre estouro de pilha Este código retorna valores distintos. No entanto, o que eu quero é retornar uma coleção fortemente tipada em oposição a um tipo anônimo ), mas se você precisar de algo genérico em todo o código, as soluções neste artigo são ótimas.
fonte
fonte
Select()
new Person
vez denew Player
? O fato de você estar solicitando deID
alguma forma não informaDistinct()
sobre o uso dessa propriedade para determinar a exclusividade; portanto, isso não funcionará.Substituir os métodos Equals (object obj) e GetHashCode () :
e depois chame:
fonte
Você deve substituir Equals on person para realmente fazer Equals on Person.id. Isso deve resultar no comportamento que você procura.
fonte
Por favor, tente com o código abaixo.
fonte