Remova duplicatas na lista usando linq

314

Eu tenho uma aula Itemscom properties (Id, Name, Code, Price).

A lista de Itemsé preenchida com itens duplicados.

Por exemplo:

1         Item1       IT00001        $100
2         Item2       IT00002        $200
3         Item3       IT00003        $150
1         Item1       IT00001        $100
3         Item3       IT00003        $150

Como remover as duplicatas da lista usando linq?

Prasad
fonte
Eu tenho outra classe como propriedade nos itens de classe também
Prasad
Você também pode fazer var set = new HashSet<int>(); var uniques = items.Where(x => set.Add(x.Id));. Deve ser criminoso para fazê-lo ..
Nawfal

Respostas:

394
var distinctItems = items.Distinct();

Para corresponder apenas a algumas das propriedades, crie um comparador de igualdade personalizado, por exemplo:

class DistinctItemComparer : IEqualityComparer<Item> {

    public bool Equals(Item x, Item y) {
        return x.Id == y.Id &&
            x.Name == y.Name &&
            x.Code == y.Code &&
            x.Price == y.Price;
    }

    public int GetHashCode(Item obj) {
        return obj.Id.GetHashCode() ^
            obj.Name.GetHashCode() ^
            obj.Code.GetHashCode() ^
            obj.Price.GetHashCode();
    }
}

Em seguida, use-o assim:

var distinctItems = items.Distinct(new DistinctItemComparer());
Christian Hayter
fonte
Olá Christian, Qual será a alteração no código se eu tiver uma lista <my_Custom_Class> e List <string>. Minha classe personalizada possui vários itens nos quais um é o número DCN e a lista <string> possui apenas o número DCN. Portanto, preciso verificar se a lista <Classe_Custom> contém qualquer dcn da lista <string>. Por exemplo, suponha Lista1 = Lista <Classe_Custom> e Lista2 = Lista <>. Se a Lista1 tiver 2000 itens e a lista2 tiver 40000 itens nos quais 600 itens da Lista1 existem na Lista2. Então, neste caso, eu preciso de 1400 como minha lista de saída como list1. Então, qual seria a expressão. Obrigado antecipadamente
Também há mais um caso, uma vez que a Lista1 contém vários itens, outros valores de itens podem ser diferentes, mas o DCN deve ser o mesmo. Portanto, no meu caso, a Distinct falhou em fornecer o resultado desejado.
2
Acho as classes comparadoras extremamente úteis. Eles podem expressar lógica além de simples comparações de nomes de propriedades. Eu escrevi um novo mês passado, para fazer algo que GroupBynão podia.
Christian Hayter
Funciona bem e me levou a aprender algo novo e investigar o XoRoperador ^em C #. Tinha usado no VB.NET via, Xormas tinha que fazer uma olhada duas vezes no seu código para ver o que era inicialmente.
atconway
Este é o erro que recebo ao tentar usar o Distinct Comparer: "O LINQ to Entities não reconhece o método 'System.Linq.IQueryable 1[DataAccess.HR.Dao.CCS_LOCATION_TBL] Distinct[CCS_LOCATION_TBL](System.Linq.IQueryable1 [DataAccess.HR.Dao.CCS_LOCATION_TBL], System.Collections.Generic.IEqualityComparer`1 [ DataAccess.HR.Dao.CCS_LOCATION_TBL]) ', e esse método não pode ser convertido em uma expressão de armazenamento.
user8128167
601
var distinctItems = items.GroupBy(x => x.Id).Select(y => y.First());
Freddy
fonte
28
Graças - estava olhando para evitar escrever uma classe comparer por isso estou feliz funciona este :)
Jen
8
+1 Esta solução permite até um desempatador: elimine duplicatas com critérios!
Adriano Carneiro
4
Mas um pouco acima!
Amirhossein Mehrvarzi
1
Mas, como Victor Juri sugeriu abaixo: use FirstorDefault. não posso acreditar, essa solução pode ser tão simples (sem comparador de igualdade personalizado)
CyberHawk
6
Você pode agrupar com várias propriedades: List <XYZ> MyUniqueList = MyList.GroupBy (x => new {x.Column1, x.Column2}). Selecione (g => g.First ()). ToList ();
Sumit Joshi
41

Se houver algo que está descartando sua consulta Distinct, convém consultar o MoreLinq e usar o operador DistinctBy e selecionar objetos distintos por ID.

var distinct = items.DistinctBy( i => i.Id );
tvanfosson
fonte
1
Não há método DistinctBy () com Linq.
Fereydoon Barikzehy
7
@FereydoonBarikzehy Mas ele não está falando sobre o Linq puro. No post é linq para o projeto MoreLinq ...
Ademar
30

Foi assim que consegui me agrupar com o Linq. Espero que ajude.

var query = collection.GroupBy(x => x.title).Select(y => y.FirstOrDefault());
Victor Juri
fonte
3
@nawfal, eu estava sugerindo FirstOrDefault () em vez de Primeira ()
sobelito
23
Se eu estiver correto, usando FirstOrDefaultaqui não oferece nenhum benefício se o Selectsegue imediatamente GroupBy, já que não há possibilidade de haver um grupo vazio (os grupos foram apenas derivada de conteúdo da coleção)
Roy Tinker
17

Use, Distinct()mas lembre-se de que ele usa o comparador de igualdade padrão para comparar valores; portanto, se você quiser algo além disso, precisará implementar seu próprio comparador.

Consulte http://msdn.microsoft.com/en-us/library/bb348436.aspx para obter um exemplo.

Brian Rasmussen
fonte
Devo observar que o comparador padrão funciona se os tipos de membros da coleção forem um dos tipos de valor. Mas qual comparador de igualdade padrão seleciona por csc para tipos de referência. Os tipos de referência devem ter comparador (es) próprio (s).
Nuri YILMAZ 03/03
16

Você tem três opções aqui para remover itens duplicados da sua lista:

  1. Use um comparador de igualdade personalizado e, em seguida, use Distinct(new DistinctItemComparer())como @Christian Hayter mencionado.
  2. Use GroupBy, mas observe que GroupByvocê deve Agrupar por todas as colunas, porque, se você apenas agrupar por Idelas, nem sempre remove itens duplicados. Por exemplo, considere o seguinte exemplo:

    List<Item> a = new List<Item>
    {
        new Item {Id = 1, Name = "Item1", Code = "IT00001", Price = 100},
        new Item {Id = 2, Name = "Item2", Code = "IT00002", Price = 200},
        new Item {Id = 3, Name = "Item3", Code = "IT00003", Price = 150},
        new Item {Id = 1, Name = "Item1", Code = "IT00001", Price = 100},
        new Item {Id = 3, Name = "Item3", Code = "IT00003", Price = 150},
        new Item {Id = 3, Name = "Item3", Code = "IT00004", Price = 250}
    };
    var distinctItems = a.GroupBy(x => x.Id).Select(y => y.First());

    O resultado para este agrupamento será:

    {Id = 1, Name = "Item1", Code = "IT00001", Price = 100}
    {Id = 2, Name = "Item2", Code = "IT00002", Price = 200}
    {Id = 3, Name = "Item3", Code = "IT00003", Price = 150}

    O que está incorreto porque é considerado {Id = 3, Name = "Item3", Code = "IT00004", Price = 250}duplicado. Portanto, a consulta correta seria:

    var distinctItems = a.GroupBy(c => new { c.Id , c.Name , c.Code , c.Price})
                         .Select(c => c.First()).ToList();

    Substituir Equale GetHashCodena classe de item:

    public class Item
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Code { get; set; }
        public int Price { get; set; }
    
        public override bool Equals(object obj)
        {
            if (!(obj is Item))
                return false;
            Item p = (Item)obj;
            return (p.Id == Id && p.Name == Name && p.Code == Code && p.Price == Price);
        }
        public override int GetHashCode()
        {
            return String.Format("{0}|{1}|{2}|{3}", Id, Name, Code, Price).GetHashCode();
        }
    }

    Então você pode usá-lo assim:

    var distinctItems = a.Distinct();
Salah Akbari
fonte
11

Um método de extensão universal:

public static class EnumerableExtensions
{
    public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> enumerable, Func<T, TKey> keySelector)
    {
        return enumerable.GroupBy(keySelector).Select(grp => grp.First());
    }
}

Exemplo de uso:

var lstDst = lst.DistinctBy(item => item.Key);
PARA MIM
fonte
Abordagem muito limpa
Steven Ryssaert
4

Experimente este método de extensão. Espero que isso possa ajudar.

public static class DistinctHelper
{
    public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
    {
        var identifiedKeys = new HashSet<TKey>();
        return source.Where(element => identifiedKeys.Add(keySelector(element)));
    }
}

Uso:

var outputList = sourceList.DistinctBy(x => x.TargetProperty);
Kent Aguilar
fonte
3
List<Employee> employees = new List<Employee>()
{
    new Employee{Id =1,Name="AAAAA"}
    , new Employee{Id =2,Name="BBBBB"}
    , new Employee{Id =3,Name="AAAAA"}
    , new Employee{Id =4,Name="CCCCC"}
    , new Employee{Id =5,Name="AAAAA"}
};

List<Employee> duplicateEmployees = employees.Except(employees.GroupBy(i => i.Name)
                                             .Select(ss => ss.FirstOrDefault()))
                                            .ToList();
Arun Kumar
fonte
0

Outra solução alternativa, não é bonito comprar viável.

Eu tenho um arquivo XML com um elemento chamado "MEMDES" com dois atributos como "GRADE" e "SPD" para registrar as informações do módulo de RAM. Existem muitos itens duplos no SPD.

Então, aqui está o código que eu uso para remover os itens duplicados:

        IEnumerable<XElement> MList =
            from RAMList in PREF.Descendants("MEMDES")
            where (string)RAMList.Attribute("GRADE") == "DDR4"
            select RAMList;

        List<string> sellist = new List<string>();

        foreach (var MEMList in MList)
        {
            sellist.Add((string)MEMList.Attribute("SPD").Value);
        }

        foreach (string slist in sellist.Distinct())
        {
            comboBox1.Items.Add(slist);
        }
Rex Hsu
fonte
-1

Quando você não deseja escrever o IEqualityComparer, tente algo como o seguinte.

 class Program
{

    private static void Main(string[] args)
    {

        var items = new List<Item>();
        items.Add(new Item {Id = 1, Name = "Item1"});
        items.Add(new Item {Id = 2, Name = "Item2"});
        items.Add(new Item {Id = 3, Name = "Item3"});

        //Duplicate item
        items.Add(new Item {Id = 4, Name = "Item4"});
        //Duplicate item
        items.Add(new Item {Id = 2, Name = "Item2"});

        items.Add(new Item {Id = 3, Name = "Item3"});

        var res = items.Select(i => new {i.Id, i.Name})
            .Distinct().Select(x => new Item {Id = x.Id, Name = x.Name}).ToList();

        // now res contains distinct records
    }



}


public class Item
{
    public int Id { get; set; }

    public string Name { get; set; }
}
Kundan Bhati
fonte