Supprimer les doublons de la liste à l'aide de linq

314

J'ai un cours Itemsavec properties (Id, Name, Code, Price).

La liste Itemsest remplie d'éléments en double.

Par exemple:

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

Comment supprimer les doublons de la liste en utilisant linq?

Prasad
la source
J'ai également une autre classe en tant que propriété dans la classe Items
Prasad
Vous pouvez aussi le faire var set = new HashSet<int>(); var uniques = items.Where(x => set.Add(x.Id));. Cela devrait être criminel de le faire ..
nawfal

Réponses:

394
var distinctItems = items.Distinct();

Pour faire correspondre uniquement certaines propriétés, créez un comparateur d'égalité personnalisé, par exemple:

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

Ensuite, utilisez-le comme ceci:

var distinctItems = items.Distinct(new DistinctItemComparer());
Christian Hayter
la source
Salut Christian, Quel sera le changement de code si j'ai une liste <my_Custom_Class> et une liste <string>. Ma classe personnalisée comporte divers éléments dont l'un est le numéro DCN et la liste <chaîne> n'a que le numéro DCN. J'ai donc besoin de vérifier que la liste <Custom_Class> contient n'importe quel dcn de la liste <chaîne>. Par exemple, supposez List1 = List <Custom_Class> et List2 = List <String>. Si List1 a 2000 éléments et list2 a 40000 éléments sur lesquels 600 éléments de List1 existent dans List2. Dans ce cas, j'ai besoin de 1400 comme liste de sortie comme list1. Quelle serait donc l'expression. Merci d'avance
Un autre cas est également présent puisque List1 contient divers éléments, les autres valeurs des éléments peuvent être différentes mais le DCN doit être le même. Donc, dans mon cas, Distinct n'a pas donné le résultat souhaité.
2
Je trouve les classes de comparaison extrêmement utiles. Ils peuvent exprimer une logique autre que de simples comparaisons de noms de propriété. J'en ai écrit un nouveau le mois dernier, pour faire quelque chose qui GroupByne pouvait pas.
Christian Hayter
Fonctionne bien et m'a permis d'apprendre quelque chose de nouveau et d'enquêter sur l' XoRopérateur ^en C #. Avait utilisé dans VB.NET via Xormais a dû faire une double prise à votre code pour voir ce qu'il était au début.
atconway
Voici l'erreur que j'obtiens lorsque j'essaie d'utiliser Distinct Comparer: "LINQ to Entities ne reconnaît pas la méthode '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]) ', et cette méthode ne peut pas être traduite en une expression de magasin.
user8128167
601
var distinctItems = items.GroupBy(x => x.Id).Select(y => y.First());
Freddy
la source
28
Merci - cherchait à éviter d'écrire une classe de comparaison, donc je suis content que cela fonctionne :)
Jen
8
+1 Cette solution permet même un bris d'égalité: éliminez les doublons avec les critères!
Adriano Carneiro
4
Mais un peu de frais généraux!
Amirhossein Mehrvarzi du
1
Mais, comme Victor Juri l'a suggéré ci-dessous: utilisez FirstorDefault. ne peut pas croire, cette solution peut être si simple (sans comparateur d'égalité personnalisé)
CyberHawk
6
Vous pouvez regrouper avec plusieurs propriétés: List <XYZ> MyUniqueList = MyList.GroupBy (x => new {x.Column1, x.Column2}). Select (g => g.First ()). ToList ();
Sumit Joshi
41

S'il y a quelque chose qui jette votre requête Distinct, vous voudrez peut-être regarder MoreLinq et utiliser l'opérateur DistinctBy et sélectionner des objets distincts par id.

var distinct = items.DistinctBy( i => i.Id );
tvanfosson
la source
1
Il n'y a pas de méthode DistinctBy () avec Linq.
Fereydoon Barikzehy
7
@FereydoonBarikzehy Mais il ne parle pas de pur Linq. En poste est linq au projet MoreLinq ...
Ademar
30

C'est ainsi que j'ai pu me regrouper avec Linq. J'espère que ça aide.

var query = collection.GroupBy(x => x.title).Select(y => y.FirstOrDefault());
Victor Juri
la source
3
@nawfal, je proposais FirstOrDefault () au lieu de First ()
sobelito
23
Si je ne me trompe pas, l'utilisation FirstOrDefaultici n'offre aucun avantage si elle Selectsuit immédiatement GroupBy, car il n'y a aucune possibilité qu'il y ait un groupe vide (les groupes viennent simplement du contenu de la collection)
Roy Tinker
17

Utilisez Distinct()mais gardez à l'esprit qu'il utilise le comparateur d'égalité par défaut pour comparer les valeurs, donc si vous voulez autre chose que cela, vous devez implémenter votre propre comparateur.

Veuillez consulter http://msdn.microsoft.com/en-us/library/bb348436.aspx pour un exemple.

Brian Rasmussen
la source
Je dois noter que le comparateur par défaut fonctionne si les types de membres de collection sont l'un des types de valeur. Mais quel comparateur d'égalité par défaut sélectionner par csc pour les types de référence. Les types de référence doivent avoir leurs propres comparateurs.
Nuri YILMAZ
16

Vous avez trois options ici pour supprimer l'élément en double dans votre liste:

  1. Utilisez un comparateur d'égalité personnalisé, puis utilisez Distinct(new DistinctItemComparer())comme @Christian Hayter mentionné.
  2. Utilisez GroupBy, mais veuillez noter que GroupByvous devez regrouper toutes les colonnes, car si vous les regroupez, Idcela ne supprime pas toujours les éléments en double. Par exemple, considérons l'exemple suivant:

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

    Le résultat de ce regroupement sera:

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

    Ce qui est incorrect car il est considéré {Id = 3, Name = "Item3", Code = "IT00004", Price = 250}comme un doublon. Donc, la bonne requête serait:

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

    3. Remplacer Equalet GetHashCodedans la classe d'objets:

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

    Ensuite, vous pouvez l'utiliser comme ceci:

    var distinctItems = a.Distinct();
Salah Akbari
la source
12

Une méthode d'extension universelle:

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

Exemple d'utilisation:

var lstDst = lst.DistinctBy(item => item.Key);
TOL
la source
Approche très propre
Steven Ryssaert
5

Essayez cette méthode d'extension. J'espère que cela pourrait aider.

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

Usage:

var outputList = sourceList.DistinctBy(x => x.TargetProperty);
Kent Aguilar
la source
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
la source
0

Une autre solution de contournement, pas beau acheter réalisable.

J'ai un fichier XML avec un élément appelé "MEMDES" avec deux attributs "GRADE" et "SPD" pour enregistrer les informations du module RAM. Il y a beaucoup d'articles en double dans SPD.

Voici donc le code que j'utilise pour supprimer les éléments dupliqués:

        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
la source
-1

Lorsque vous ne voulez pas écrire IEqualityComparer, vous pouvez essayer quelque chose comme suit.

 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
la source