Rimuovere i duplicati nell'elenco usando linq


314

Ho una lezione Itemscon properties (Id, Name, Code, Price).

L'elenco di Itemsè popolato con elementi duplicati.

Per es .:

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

Come rimuovere i duplicati nell'elenco usando linq?


Ho un'altra classe come proprietà anche nella classe Items
Prasad,

Puoi anche fare var set = new HashSet<int>(); var uniques = items.Where(x => set.Add(x.Id));. Dovrebbe essere criminale farlo ..
nawfal,

Risposte:


394
var distinctItems = items.Distinct();

Per abbinare solo alcune proprietà, crea un comparatore di uguaglianza personalizzato, ad esempio:

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();
    }
}

Quindi usalo in questo modo:

var distinctItems = items.Distinct(new DistinctItemComparer());

Ciao Christian, quale sarà la modifica del codice se ho una lista <mia_Custom_Class> e una lista <stringa>. La mia classe personalizzata ha vari elementi in cui uno è il numero DCN e l'elenco <stringa> ha solo il numero DCN. Quindi ho bisogno di controllare la lista <Custom_Class> contiene qualsiasi dcn dalla lista <string>. Ad esempio supponiamo che List1 = List <Custom_Class> e List2 = List <String>. Se List1 ha 2000 articoli e list2 ha 40000 articoli per i quali 600 elementi da List1 esistono in List2. Quindi in questo caso ho bisogno di 1400 come Elenco di output come Elenco1. Quindi quale sarebbe l'espressione. Grazie in anticipo

Anche un altro caso è qui poiché List1 contiene vari elementi, altri valori degli oggetti potrebbero essere diversi ma il DCN deve essere lo stesso. Quindi, nel mio caso, Distinct non è riuscito a dare il risultato desiderato.

2
Trovo le classi di confronto estremamente utili. Possono esprimere una logica diversa dal semplice confronto di nomi di proprietà. Ne ho scritto uno nuovo il mese scorso, per fare qualcosa che GroupBynon poteva.
Christian Hayter,

Funziona bene e mi ha fatto imparare qualcosa di nuovo e indagare XoRsull'operatore ^in C #. Avevo usato in VB.NET tramite Xorma dovevo fare un doppio giro al tuo codice per vedere com'era all'inizio.
atconway

Questo è l'errore che ottengo quando provo a utilizzare Distinct Comparer: "LINQ to Entities non riconosce il metodo '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 questo metodo non può essere tradotto in un'espressione di archivio.
user8128167

601
var distinctItems = items.GroupBy(x => x.Id).Select(y => y.First());

28
Grazie - cercavo di evitare di scrivere una classe di confronto, quindi sono contento che funzioni :)
Jen,

8
+1 Questa soluzione consente anche un pareggio: elimina i duplicati con criteri!
Adriano Carneiro,

4
Ma un po 'in testa!
Amirhossein Mehrvarzi,

1
Ma, come suggerito da Victor Juri di seguito: usa FirstorDefault. non ci posso credere, quella soluzione può essere così semplice (senza comparatore di uguaglianza personalizzato)
CyberHawk

6
Puoi raggruppare con più proprietà: Elenco <XYZ> MyUniqueList = MyList.GroupBy (x => new {x.Column1, x.Column2}). Seleziona (g => g.First ()). ToList ();
Summit Joshi,

41

Se c'è qualcosa che sta gettando via la tua query Distinct, potresti voler guardare MoreLinq e usare l'operatore DistinctBy e selezionare oggetti distinti per id.

var distinct = items.DistinctBy( i => i.Id );

1
Non esiste un metodo DistinctBy () con Linq.
Fereydoon Barikzehy,

7
@FereydoonBarikzehy Ma non sta parlando di puro Linq. In post è linq al progetto MoreLinq ...
Ademar

30

È così che sono riuscito a raggrupparmi con Linq. Spero che sia d'aiuto.

var query = collection.GroupBy(x => x.title).Select(y => y.FirstOrDefault());

3
@nawfal, stavo suggerendo FirstOrDefault () al posto di First ()
sobelito,

23
Se ho ragione, l'uso FirstOrDefaultqui non offre alcun vantaggio se Selectsegue immediatamente GroupBy, poiché non esiste la possibilità che esista un gruppo vuoto (i gruppi sono stati appena derivati dai contenuti della raccolta)
Roy Tinker,

17

Utilizzare Distinct()ma tenere presente che utilizza il comparatore di uguaglianza predefinito per confrontare i valori, quindi se si desidera qualcosa di diverso, è necessario implementare il proprio comparatore.

Per un esempio, vedere http://msdn.microsoft.com/en-us/library/bb348436.aspx .


Dovrei notare che il comparatore predefinito funziona se i tipi di membri di raccolta sono uno dei tipi di valore. Ma quale comparatore di uguaglianza predefinito seleziona csc per i tipi di riferimento. I tipi di riferimento devono avere i propri comparatori.
Nuri YILMAZ,

16

Hai tre opzioni qui per rimuovere elementi duplicati nel tuo elenco:

  1. Utilizzare un comparatore di uguaglianza personalizzato e quindi utilizzare Distinct(new DistinctItemComparer())come menzionato @Christian Hayter .
  2. Usa GroupBy, ma tieni presente GroupByche dovresti raggruppare in base a tutte le colonne perché se si raggruppa solo per Idesso non rimuove sempre gli elementi duplicati. Ad esempio, considera il seguente esempio:

    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());

    Il risultato per questo raggruppamento sarà:

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

    Il che non è corretto perché considera {Id = 3, Name = "Item3", Code = "IT00004", Price = 250}duplicato. Quindi la query corretta sarebbe:

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

    3. Override Equale GetHashCodenella classe di elementi:

    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();
        }
    }

    Quindi puoi usarlo in questo modo:

    var distinctItems = a.Distinct();

12

Un metodo di estensione universale:

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());
    }
}

Esempio di utilizzo:

var lstDst = lst.DistinctBy(item => item.Key);

Approccio molto pulito
Steven Ryssaert,

5

Prova questo metodo di estensione. Spero che questo possa aiutare.

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);

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();

0

Un'altra soluzione alternativa, non bello acquistare fattibile.

Ho un file XML con un elemento chiamato "MEMDES" con due attributi come "GRADE" e "SPD" per registrare le informazioni sul modulo RAM. Ci sono molti articoli duplicati in SPD.

Quindi, ecco il codice che uso per rimuovere gli elementi in duplicazione:

        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);
        }

-1

Quando non vuoi scrivere IEqualityComparer puoi provare qualcosa come segue.

 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; }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.