2009-10-22 1 views
221

У меня есть класс Items с properties (Id, Name, Code, Price).Удалить дубликаты в списке используя linq

Список Items заполнен дублированными товарами.

Для экс .:

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

Как удалить дубликаты в списке с помощью LINQ?

+0

У меня есть еще один класс, как собственность в Items Класс также – Prasad

+0

Вы также можете сделать 'вар набора = новый HashSet (); var uniques = items.Where (x => set.Add (x.Id)); '. Это должно быть преступно для этого .. – nawfal

ответ

309
var distinctItems = items.Distinct(); 

Чтобы соответствовать только на некоторых из свойств, создать собственный компаратор равенство, например:

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

Затем использовать его как это:

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

Привет, Кем будет, что будет сменой кода, если у меня есть Список и Список . Мой пользовательский класс имеет различные элементы, в которых один номер DCN, а список имеет только номер DCN. Поэтому мне нужно проверить Список содержит любой dcn из списка . Например, предположим, что List1 = List и List2 = List . Если в List1 есть 2000 элементов, а list2 содержит 40000 элементов, из которых в List2 существует 600 элементов из списка 1. Поэтому в этом случае мне нужно 1400 в качестве моего списка результатов как list1. Итак, каково было бы выражение. Заранее спасибо –

+0

Также здесь есть еще один случай, поскольку List1 содержит различные элементы, другие значения элементов могут отличаться, но DCN должен быть таким же. Так что в моем случае Distinct не дал желаемого результата. –

+1

Я нахожу классы сравнения чрезвычайно полезными. Они могут выражать логику, отличную от простых сопоставлений имен свойств. Я написал новый в прошлом месяце, чтобы сделать что-то, чего не может сделать GroupBy. –

15

Использование Distinct(), но имейте в виду, что он использует сопоставитель равенства по умолчанию для сравнения значений, поэтому, если вам нужно что-либо помимо этого, вам нужно реализовать свой собственный компаратор.

Для примера см. http://msdn.microsoft.com/en-us/library/bb348436.aspx.

+0

Я должен заметить, что сопоставление по умолчанию работает, если типы членов коллекции являются одним из типов значений. –

31

Если есть что-то, что отбрасывает ваш запрос Distinct, вы можете посмотреть на MoreLinq и использовать оператор DistinctBy и выбрать отдельные объекты по id.

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

Нет метода DistinctBy() с Linq. –

+5

@FereydoonBarikzehy Но он не тал король о чистом Линке. В post это linq для проекта MoreLinq ... – Ademar

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

Спасибо - старался избегать написания класса сравнения, поэтому я рад, что это работает :) – Jen

+6

+1 Это решение даже позволяет использовать тай-брейк: устранить дубликаты с критериями! –

+4

Но немного накладные расходы! –

23

Это, как я был в состоянии группе с Linq. Надеюсь, поможет.

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

уже ответил .. – nawfal

+2

@nawfal, я предлагал FirstOrDefault() вместо First() – sobelito

+9

Если я прав, использование 'FirstOrDefault' здесь не принесет никакой пользы, если' Select' сразу следует за GroupBy', поскольку нет возможности там была пустая группа (группы были просто получены из содержимого коллекции) –

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

У вас есть три опции здесь для удаления дубликатов элемента в вашем списке:

  1. Используйте компаратор пользовательского равенства, а затем использовать Distinct(new DistinctItemComparer()) как @Christian Hayter упоминалось.
  2. Используйте GroupBy, но обратите внимание, что в GroupBy вам следует группировать все столбцы, потому что если вы просто группируете Id, он не удаляет повторяющиеся элементы всегда. Например, рассмотрим следующий пример:

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

    В результате для этой группировки будет:

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

    что неправильно, потому что он считает {Id = 3, Name = "Item3", Code = "IT00004", Price = 250} как дубликат. Таким образом, правильный запрос будет следующим:

    3.Override Equal и GetHashCode в классе пункт:

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

    Затем вы можете использовать его как это:

    var distinctItems = a.Distinct(); 
    
1

Попробуйте этот метод расширения вне. Надеюсь, это может помочь.

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

Использование:

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

Если вы не хотите писать IEqualityComparer вы можете попробовать что-то вроде следующего.

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

Универсальное метод расширения:

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

Пример использования:

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