Видаліть дублікати зі списку за допомогою linq


314

У мене є клас Itemsс properties (Id, Name, Code, Price).

Список Itemsзаповнений дублюючими елементами.

Наприклад:

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

Як видалити дублікати зі списку за допомогою linq?


У мене є ще один клас як майно в класі елементів
Prasad

Ви також можете зробити var set = new HashSet<int>(); var uniques = items.Where(x => set.Add(x.Id));. Зробити це слід злочинно ..
nawfal

Відповіді:


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

Привіт Крістіан, Що буде зміна коду, якщо у мене будуть Список <my_Custom_Class> та Список <string>. У моєму власному класі є різні елементи, у яких один номер DCN, а список <string> має лише номер DCN. Тому мені потрібно перевірити, що список <Custom_Class> містить будь-який dcn зі списку <string>. Наприклад, припустимо List1 = Список <Custom_Class> і List2 = Список <String>. Якщо у List1 є 2000 елементів, а list2 має 40000 елементів, у яких у списку існує 600 елементів зі списку1. Тому в цьому випадку мені потрібно 1400 як мій вихідний список як list1. То який би був вираз. Дякую заздалегідь

Також тут є ще один випадок, оскільки List1 містить різні елементи, значення інших елементів можуть бути різними, але DCN повинен бути однаковим. Тож у моєму випадку Distinct не зміг дати бажане місце.

2
Я вважаю порівняльні класи надзвичайно корисними. Вони можуть виражати логіку, відмінну від простих порівнянь імен властивостей. Я написав новий минулого місяця, щоб зробити щось, що GroupByне міг.
Крістіан Хейтер

Добре працює, і я змусив мене дізнатися щось нове і дослідити XoRоператора ^на C #. Використовував у VB.NET через, Xorале повинен був подвійно взяти свій код, щоб побачити, що це було спочатку.
atconway

Це помилка, яку я отримую, коли намагаюся використовувати Distinct Comparer: "LINQ для Entities не розпізнає метод '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]) ', і цей метод не можна перекласти в вираз магазину.
user8128167

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

28
Дякую - прагнув уникнути написання порівняльного класу, тому я радий, що це працює :)
Jen

8
+1 Це рішення навіть дозволяє зробити вимикач: усувайте дублікати з критеріями!
Адріано Карнейро

4
Але трохи накладні!
Amirhossein Mehrvarzi

1
Але, як запропонував Віктор Юрі нижче: використовуйте FirstorDefault. не можу повірити, що рішення може бути таким простим (без спеціального порівняння рівності)
CyberHawk

6
Ви можете згрупувати декілька властивостей: Список <XYZ> MyUniqueList = MyList.GroupBy (x => новий {x.Column1, x.Column2}). Виберіть (g => g.First ()). ToList ();
Суміт Джоші

41

Якщо є щось, що відкидає ваш запит Distinct, ви можете подивитися на MoreLinq і скористатися оператором DistinctBy та вибрати окремі об'єкти за ідентифікатором.

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

1
Не існує методу DistinctBy () з Linq.
Fereydoon Barikzehy

7
@FereydoonBarikzehy Але він не говорить про чистий Linq. Повідомлення посилається на проект MoreLinq ...
Адемар

30

Ось як мені вдалося згрупуватися з Linq. Сподіваюся, це допомагає.

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

3
@nawfal, я пропонував FirstOrDefault () замість First ()
sobelito

23
Якщо я маю рацію, використання FirstOrDefaultтут не приносить користі, якщо Selectнегайно випливає GroupBy, оскільки немає можливості бути порожньою групою (групи тільки були отримані зі вмісту колекції)
Roy Tinker

17

Використовуйте, Distinct()але майте на увазі, що він використовує порівняльний показник рівності за замовчуванням для порівняння значень, тому, якщо ви хочете щось більше, ніж вам, вам потрібно реалізувати власний порівняльник.

Для прикладу див. Http://msdn.microsoft.com/en-us/library/bb348436.aspx .


Я повинен зауважити, що порівняльник за замовчуванням працює, якщо типи учасників колекції є одним із типів значень. Але який порівняння рівності за замовчуванням вибрати за допомогою csc для еталонних типів. Довідкові типи повинні мати власні порівняння.
Нурі ЙІЛМАЗ

16

Тут у вас є три варіанти для видалення повторюваного елемента зі свого списку:

  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}дублікатом. Тож правильний запит був би:

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

    3.Поміщення 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();

11

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

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

Дуже чистий підхід
Стівен Ріссаерт

4

Спробуйте цей метод розширення. Сподіваємось, це може допомогти.

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

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

Ще одне вирішення проблеми, не красива покупка оброблюваного.

У мене є XML-файл з елементом під назвою "MEMDES" з двома атрибутами як "GRADE" та "SPD" для запису інформації модуля оперативної пам'яті. Дублікатів у SPD дуже багато.

Ось ось код, який я використовую для видалення продуктованих елементів:

        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

Коли ви не хочете писати 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; }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.