2009-10-22 8 views
221

J'ai une classe Items avec properties (Id, Name, Code, Price). La liste de Items est remplie avec des éléments dupliqués.Supprimer les doublons dans la liste en utilisant linq

Ex .:

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

Comment supprimer les doublons dans la liste en utilisant LINQ?

+0

J'ai une autre classe que la propriété des objets de classe aussi – Prasad

+0

Vous pouvez également faire 'var ensemble = new HashSet (); var uniques = items.Where (x => set.Add (x.Id)); '. Il devrait être criminel de le faire .. – nawfal

Répondre

309
var distinctItems = items.Distinct(); 

apparie seulement quelques-unes des propriétés, créer 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(); 
    } 
} 

utiliser ensuite comme ceci:

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

Salut Christian, Quel sera le changement de code si j'ai une liste et la liste . Ma classe personnalisée a divers éléments dans lesquels un est numéro DCN et la liste a seulement un numéro DCN. Donc, je dois vérifier la liste contient tout dcn de la liste . Par exemple supposons List1 = List et List2 = List . Si List1 a 2000 éléments et que list2 a 40000 éléments sur lesquels 600 éléments de List1 existe dans List2. Donc, dans ce cas, j'ai besoin de 1400 comme ma liste de sortie en tant que list1. Alors, quelle serait l'expression. Merci d'avance –

+0

Aussi un autre cas est ici puisque List1 contient divers éléments, d'autres éléments peuvent être différents mais le DCN doit être le même. Donc, dans mon cas, Distinct a échoué à donner envie de sortir. –

+1

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 le mois dernier pour faire quelque chose que 'GroupBy' ne pouvait pas faire. –

15

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

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

+0

Je dois remarquer que le comparateur par défaut fonctionne si les types de membre de collection sont des types de valeur. csc pour les types de référence Les types de référence doivent avoir leur (s) comparateur (s) –

31

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

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

Il n'y a pas de méthode DistinctBy() avec Linq –

+5

@FereydoonBarikzehy Mais il n'est pas roi à propos de Linq pur. Dans la publication est linq à MoreLinq projet ... – Ademar

466
var distinctItems = items.GroupBy(x => x.Id).Select(y => y.First()); 
+21

Merci - cherchait à éviter d'écrire un niveau de comparaison, donc je suis heureux que cela fonctionne :) – Jen

+6

+1 Cette solution permet même un tie-breaker: éliminer les doublons avec des critères! –

+4

Mais un peu de frais généraux! –

23

Voilà comment j'ai pu regrouper par LINQ. J'espère que cela aide.

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

déjà répondu .. – nawfal

+2

@nawfal, je suggérais FirstOrDefault() à la place de First() – sobelito

+9

Si je suis correct, l'utilisation de 'FirstOrDefault' ici n'offre aucun avantage si le' Select' suit immédiatement 'GroupBy', car il n'y a aucune possibilité de là étant un groupe vide (les groupes étaient juste dérivés du contenu de la collection) –

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

Vous avez trois options ici pour enlever l'article en double dans votre liste:

  1. Utilisez un comparateur d'égalité un personnalisé et utilisez Distinct(new DistinctItemComparer()) comme @Christian Hayter mentionné.
  2. Utilisez GroupBy, mais notez svp dans GroupBy vous devriez Grouper par toutes les colonnes parce que si vous juste groupez par Id il ne supprime pas toujours les articles 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 parce qu'il considère {Id = 3, Name = "Item3", Code = "IT00004", Price = 250} comme doublon. Ainsi, la requête correcte serait:

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

    3.Remplacer Equal et GetHashCode en classe d'article:

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

Essayez cette méthode d'extension du. 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))); 
    } 
} 

Utilisation:

var outputList = sourceList.DistinctBy(x => x.TargetProperty); 
0

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

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(g => g.Key);