Distinto no Linq com base em apenas um campo da tabela

133

Estou tentando usar .distinct no Linq para obter resultados com base em um campo da tabela (portanto, não exija um registro duplicado inteiro da tabela).

Eu sei escrever consulta básica usando distintas da seguinte maneira:

var query = (from r in table1
orderby r.Text
select r).distinct();

mas preciso de resultados onde r.textnão seja duplicado.

Megha Jain
fonte
Você precisa especificar o campo que deseja ser distinto, ver msdn.microsoft.com/en-us/library/bb348436.aspx
Antarr Byrd

Respostas:

300

Tente o seguinte:

table1.GroupBy(x => x.Text).Select(x => x.FirstOrDefault());

Isso agrupará a tabela Texte usará a primeira linha de cada grupo, resultando em linhas Textdistintas.

Daniel Hilgarth
fonte
2
E se groupby tiver mais de um campo?
6
@ user585440: Nesse caso, você usa um tipo anônimo da seguinte maneira:table1.GroupBy(x => new { x.Text, x.Property2, x.Property3 }).Select(x => x.First());
Daniel Hilgarth 14/01
2
Sim, você está certo e eu já o encontrei. Obrigado mesmo assim. E também acho que Select (x => x.First ()) pode causar falha. É melhor mudar para Select (x => x.FirstOrDefault ());
6
Eu tive que usar FirstOrDefault ou então houve um erro de execução
TruthOf42
2
@ TruthOf42 Isso é bastante improvável. GroupBynão cria grupos vazios, veja meu comentário anterior. Muito provavelmente, seu código contém mais do que você vê aqui. Talvez você tenha um Wherebem ou uma condição para o First.
Daniel Hilgarth
26

MoreLinq tem um método DistinctBy que você pode usar:

Isso permitirá que você faça:

var results = table1.DistictBy(row => row.Text);

A implementação do método (sem validação de argumento) é a seguinte:

private static IEnumerable<TSource> DistinctByImpl<TSource, TKey>(IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
{
    HashSet<TKey> knownKeys = new HashSet<TKey>(comparer);
    foreach (TSource element in source)
    {
        if (knownKeys.Add(keySelector(element)))
        {
            yield return element;
        }
    }
}
Servy
fonte
desculpe, eu não estava interessado em usar a igualdade de comparação.
perfil completo de Megha Jain
@MeghaJain Bem, um será usado independentemente, pois GroupByprecisa de um também. Ambos os métodos usarão o padrão EqualityComparerse nenhum for fornecido.
Servy
9
Bem, corrija-me se estiver errado, mas isso aqui é distinto na memória, não no DB? Isso não poderia levar a uma verificação completa indesejada?
Kek
@Kek. Não, devido ao retorno do rendimento, você irá parar no primeiro elemento distinto. Eventualmente, sim, você carregará cada chave no HashSet, mas como IEnumerable entra e IEnumerable sai, você só obterá esses itens. Se você estiver falando sobre LINQ to SQL, sim, isso fará uma varredura de tabela.
PRMan
12

mas preciso de resultados em que o r.text não seja duplicado

Parece que você quer isso:

table1.GroupBy(x => x.Text)
      .Where(g => g.Count() == 1)
      .Select(g => g.First());

Isso selecionará as linhas onde Texté único.

Tim Schmelter
fonte
7

A resposta de Daniel Hilgarth acima leva a uma System.NotSupportedexceção Com o Entity-Framework . Com o Entity-Framework , ele deve ser:

table1.GroupBy(x => x.Text).Select(x => x.FirstOrDefault());
Biraj Saha
fonte
3

Há muitas discussões sobre esse tópico.

Você pode encontrar um deles aqui :

Uma das sugestões mais populares foi o método Distinct, usando uma expressão lambda como parâmetro, como o @Servy apontou.

O arquiteto chefe de C #, Anders Hejlsberg, sugeriu a solução aqui . Também explicando por que a equipe de design da estrutura decidiu não adicionar uma sobrecarga do método Distinct, que usa uma lambda.

TKharaishvili
fonte
2

Pelo que encontrei, sua consulta está correta. Basta alterar "selecione r" para "selecionar r.Texto" é tudo e isso deve resolver o problema. É assim que o MSDN documentou como deve funcionar.

Ex:

    var query = (from r in table1 orderby r.Text select r.Text).distinct();
Josh Parks
fonte
você alterou a instrução "select" que pode não ser desejada neste caso
faza
1
data.Select(x=>x.Name).Distinct().Select(x => new SelectListItem { Text = x });
bgS
fonte
-2

tente este código:

table1.GroupBy(x => x.Text).Select(x => x.FirstOrDefault());
HamidReza
fonte
-5

Você pode tentar isso:table1.GroupBy(t => t.Text).Select(shape => shape.r)).Distinct();

LucaGuerra
fonte