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.