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?

Prasad
źródło
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());
Christian Hayter
źródło
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());
Freddy
źródło
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 );
tvanfosson
źródło
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());
Victor Juri
źródło
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.

Brian Rasmussen
źródło
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();
Salah Akbari
źródło
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);
TOL
źródło
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);
Kent Aguilar
źródło
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();
Arun Kumar
źródło
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);
        }
Rex Hsu
źródło
-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; }
}
Kundan Bhati
źródło