Usuń duplikaty z listy za pomocą linq


314

Mam zajęcia Itemsz properties (Id, Name, Code, Price).

Lista Itemsjest wypełniona zduplikowanymi elementami.

Na przykład:

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

Jak usunąć duplikaty z listy za pomocą linq?


Mam także inną klasę jako własność w klasie przedmiotów
— Prasad,

Możesz też zrobić var set = new HashSet<int>(); var uniques = items.Where(x => set.Add(x.Id));. To powinno być kryminalne.
— nawfal

Odpowiedzi:


394
var distinctItems = items.Distinct();

Aby dopasować tylko niektóre właściwości, utwórz niestandardowy moduł porównujący równość, np .:

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

Następnie użyj tego w następujący sposób:

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

Cześć Christian, Jaka będzie zmiana w kodzie, jeśli mam List <my_Custom_Class> i List <string>. Moja klasa niestandardowa ma różne elementy, w których jeden jest numerem DCN, a lista <ciąg> ma tylko numer DCN. Więc muszę sprawdzić, czy Lista <Custom_Class> zawiera dowolny numer dcn z Listy <ciąg>. Załóżmy na przykład List1 = List <Custom_Class> i List2 = List <String>. Jeśli lista 1 zawiera 2000 pozycji, a lista 2 zawiera 40000 pozycji, na których 600 pozycji z listy 1 istnieje na liście 2. Więc w tym przypadku potrzebuję 1400 jako mojej listy wyjściowej List as list1. Jakie byłoby to wyrażenie. Z góry

Jest jeszcze jeden przypadek, ponieważ Lista1 zawiera różne elementy, inne wartości elementów mogą być różne, ale DCN musi być taka sama. Więc w moim przypadku Distinct nie dał pożądanego wyjścia.

2
Uważam, że klasy porównawcze są niezwykle przydatne. Mogą wyrażać logikę inną niż proste porównanie nazw właściwości. W zeszłym miesiącu napisałem nowy, aby zrobić coś, GroupByczego nie można.
— Christian Hayter,

Działa dobrze i kazał mi nauczyć się czegoś nowego i zbadać XoRoperatora ^w C #. Używał w VB.NET za pośrednictwem, Xorale musiał zrobić podwójne podejście do kodu, aby zobaczyć, co to było na początku.
— atconway

Jest to błąd, który pojawia się, gdy próbuję użyć programu Distinct Comparer: „LINQ to Entities nie rozpoznaje metody„ System.Linq.IQueryable 1[DataAccess.HR.Dao.CCS_LOCATION_TBL] Distinct[CCS_LOCATION_TBL](System.Linq.IQueryable1 [DataAccess.HR. taj.CCS_LOCATION_TBL ], System.Collections.Generic.IEqualityComparer`1 [ DataAccess.HR.resent.CCS_LOCATION_TBL]) ”, a tej metody nie można przetłumaczyć na wyrażenie sklepu.
— user8128167,

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

28
Dzięki - starałem się uniknąć pisania klasy porównawczej, więc cieszę się, że to działa :)
— Jen

8
+1 To rozwiązanie pozwala nawet na remis: eliminuj duplikaty z kryteriami!
— Adriano Carneiro,

4
Ale trochę nad głową!
— Amirhossein Mehrvarzi

1
Ale, jak sugerował poniżej Victor Juri: użyj FirstorDefault. nie mogę uwierzyć, że to rozwiązanie może być tak proste (bez niestandardowego
— modułu porównującego

6
Możesz grupować z wieloma właściwościami: List <XYZ> MyUniqueList = MyList.GroupBy (x => new {x.Column1, x.Column2}). Wybierz (g => g.First ()). ToList ();
— Sumit Joshi,

41

Jeśli jest coś, co odrzuca zapytanie Distinct, możesz spojrzeć na MoreLinq i użyć operatora DistinctBy i wybrać odrębne obiekty według identyfikatora.

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

1
Nie ma metody DistinctBy () z Linq.
— Fereydoon Barikzehy

7
@FereydoonBarikzehy Ale on nie mówi o czystym Linq. W poście jest linq do projektu MoreLinq ...
— Ademar,

30

W ten sposób mogłem grupować się z Linq. Mam nadzieję, że to pomoże.

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

3
@nawfal, sugerowałem FirstOrDefault () zamiast First ()
— sobelito

23
Jeśli mam rację, użycie FirstOrDefaulttutaj nie przynosi korzyści, jeśli Selectnastąpi natychmiast GroupBy, ponieważ nie ma możliwości, aby istniała pusta grupa (grupy zostały wyprowadzone z zawartości kolekcji)
— Roy Tinker,

17

Użyj, Distinct()ale pamiętaj, że używa domyślnego modułu porównującego do porównywania wartości, więc jeśli chcesz czegoś poza tym, musisz wdrożyć własny moduł porównujący.

Proszę zobaczyć http://msdn.microsoft.com/en-us/library/bb348436.aspx dla przykładu.


Powinienem zauważyć, że domyślny moduł porównujący działa, jeśli typy elementów kolekcji są jednym z typów wartości. Ale który domyślny moduł porównujący równość wybiera csc dla typów referencyjnych. Typy referencyjne muszą mieć własnego urządzenia porównującego.
— Nuri YILMAZ

16

Masz tutaj trzy możliwości usunięcia zduplikowanego elementu z listy:

  1. Użyj niestandardowego programu porównującego równość, a następnie użyj go, Distinct(new DistinctItemComparer())jak wspomniano w @Christian Hayter .
  2. Użyj GroupBy, ale pamiętaj, GroupByże powinieneś pogrupować według wszystkich kolumn, ponieważ jeśli tylko pogrupujesz według Id, nie zawsze usunie zduplikowane elementy. Na przykład rozważ następujący przykład:

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

    Wynik dla tego grupowania będzie następujący:

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

    Co jest nieprawidłowe, ponieważ uważa się je za {Id = 3, Name = "Item3", Code = "IT00004", Price = 250}duplikat. Prawidłowe zapytanie to:

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

    3. Zastąpienie Equaliw GetHashCodeklasie przedmiotów:

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

    Następnie możesz użyć tego w następujący sposób:

    var distinctItems = a.Distinct();

11

Uniwersalna metoda rozszerzenia:

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

Przykład użycia:

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

Bardzo czyste podejście
— Steven Ryssaert

4

Wypróbuj tę metodę rozszerzenia. Mam nadzieję, że to może pomóc.

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

Stosowanie:

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

Kolejne obejście, nie piękny zakup wykonalny.

Mam plik XML z elementem o nazwie „MEMDES” z dwoma atrybutami „GRADE” i „SPD” do rejestrowania informacji o module pamięci RAM. W SPD jest wiele duplikatów.

Oto kod, którego używam do usuwania duplikatów:

        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

Jeśli nie chcesz pisać IEqualityComparer, możesz spróbować czegoś takiego.

 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; }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.