Xóa các bản sao trong danh sách bằng linq


314

Tôi có một lớp Itemsvới properties (Id, Name, Code, Price).

Danh sách Itemsđược điền với các mục trùng lặp.

Ví dụ:

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

Làm thế nào để loại bỏ các bản sao trong danh sách bằng cách sử dụng linq?


Tôi cũng có một lớp khác là tài sản trong Lớp Vật phẩm
Prasad

Bạn cũng có thể làm var set = new HashSet<int>(); var uniques = items.Where(x => set.Add(x.Id));. Nó nên là tội phạm để làm như vậy ..
nawfal

Câu trả lời:


394
var distinctItems = items.Distinct();

Để chỉ khớp với một số thuộc tính, hãy tạo một bộ so sánh đẳng thức tùy chỉnh, ví dụ:

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

Sau đó sử dụng nó như thế này:

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

Xin chào Christian, Điều gì sẽ thay đổi mã nếu tôi có Danh sách <my_Custom_Class> và Danh sách <chuỗi>. Lớp tùy chỉnh của tôi có nhiều mục khác nhau trong đó một mục là số DCN và danh sách <chuỗi> chỉ có số DCN. Vì vậy, tôi cần kiểm tra Danh sách <Custom_Class> chứa bất kỳ dcn nào từ Danh sách <chuỗi>. Ví dụ: giả sử List1 = List <Custom_Class> và List2 = List <String>. Nếu List1 có 2000 mục và list2 có 40000 mục trong đó 600 mục từ List1 tồn tại trong List2. Vì vậy, trong trường hợp này tôi cần 1400 là danh sách đầu ra của tôi là list1. Vì vậy, những gì sẽ được biểu hiện. Cảm ơn trước

Ngoài ra, một trường hợp nữa ở đây vì List1 chứa nhiều mặt hàng khác nhau, các giá trị vật phẩm khác có thể khác nhưng DCN phải giống nhau. Vì vậy, trong trường hợp của tôi, riêng biệt không thể đưa ra mong muốn.

2
Tôi thấy các lớp so sánh cực kỳ hữu ích. Họ có thể diễn đạt logic khác với so sánh tên thuộc tính đơn giản. Tôi đã viết một cái mới vào tháng trước, để làm một cái gì đó GroupBykhông thể.
Christian Hayter

Hoạt động tốt và giúp tôi học một cái gì đó mới và điều tra XoRtoán tử ^trong C #. Đã được sử dụng trong VB.NET thông qua Xornhưng phải thực hiện gấp đôi mã của bạn để xem nó là gì lúc đầu.
atconway

Đây là lỗi tôi gặp phải khi cố gắng sử dụng Trình so sánh riêng biệt: "LINQ to Entities không nhận ra phương thức '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 [ Phương thức DataAccess.HR.Dao.CCS_LOCATION_TBL]) 'và phương thức này không thể được dịch thành biểu thức lưu trữ.
user8128167

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

28
Cảm ơn - đã tìm cách tránh viết một lớp so sánh vì vậy tôi rất vui vì công việc này :)
Jen

8
+1 Giải pháp này thậm chí còn cho phép một bộ ngắt kết nối: loại bỏ trùng lặp với các tiêu chí!
Adriano Carneiro

4
Nhưng một chút chi phí!
Amirhossein Mehrvarzi

1
Nhưng, như Victor Juri đã đề xuất dưới đây: sử dụng FirstorDefault. không thể tin được, giải pháp đó có thể đơn giản như vậy (không có bộ so sánh bình đẳng tùy chỉnh)
CyberHawk

6
Bạn có thể nhóm với nhiều thuộc tính: List <XYZ> MyUniqueList = MyList.groupBy (x => new {x.Column1, x.Column2}). Chọn (g => g.First ()). ToList ();
Sumit Joshi

41

Nếu có một cái gì đó đang loại bỏ truy vấn riêng biệt của bạn, bạn có thể muốn xem MoreLinq và sử dụng toán tử DistincBy và chọn các đối tượng riêng biệt theo id.

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

1
Không có phương thức DistincBy () với Linq.
Fereydoon Barikzehy

7
@FereydoonBarikzehy Nhưng anh ấy không nói về Linq thuần túy. Trong bài là linq cho dự án MoreLinq ...
Ademar

30

Đây là cách tôi có thể nhóm với Linq. Hy vọng nó giúp.

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

3
@nawfal, tôi đã đề xuất FirstOrDefault () thay cho First ()
sobelito

23
Nếu tôi đúng, sử dụng FirstOrDefaultở đây sẽ không có lợi nếu Selectngay lập tức GroupBy, vì không có khả năng có một nhóm trống (các nhóm chỉ xuất phát từ nội dung của bộ sưu tập)
Roy Tinker

17

Sử dụng Distinct()nhưng hãy nhớ rằng nó sử dụng trình so sánh đẳng thức mặc định để so sánh các giá trị, vì vậy nếu bạn muốn bất cứ điều gì ngoài đó bạn cần phải thực hiện trình so sánh của riêng mình.

Vui lòng xem http://msdn.microsoft.com/en-us/l Library / bb348436.aspx để biết ví dụ.


Tôi nên lưu ý rằng bộ so sánh mặc định hoạt động nếu các loại thành viên bộ sưu tập là một trong các loại giá trị. Nhưng so sánh đẳng thức mặc định nào được chọn bởi csc cho các loại tham chiếu. Các loại tham chiếu phải có (các) bộ so sánh riêng.
Nuri YILMAZ ngày

16

Bạn có ba tùy chọn ở đây để xóa mục trùng lặp trong Danh sách của mình:

  1. Sử dụng aa so sánh đẳng thức tùy chỉnh và sau đó sử dụng Distinct(new DistinctItemComparer())như @Christian Hayter đã đề cập.
  2. Sử dụng GroupBy, nhưng xin lưu ý rằng GroupBybạn nên Nhóm theo tất cả các cột vì nếu bạn chỉ nhóm theo Idnó sẽ không xóa các mục trùng lặp luôn. Ví dụ, hãy xem xét ví dụ sau:

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

    Kết quả cho nhóm này sẽ là:

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

    Điều này là không chính xác bởi vì nó được coi {Id = 3, Name = "Item3", Code = "IT00004", Price = 250}là trùng lặp. Vì vậy, truy vấn chính xác sẽ là:

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

    3.Override EqualGetHashCodetrong lớp mục:

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

    Sau đó, bạn có thể sử dụng nó như thế này:

    var distinctItems = a.Distinct();

11

Một phương pháp mở rộng phổ quát:

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

Ví dụ về cách sử dụng:

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

Cách tiếp cận rất sạch sẽ
Steven Ryssaert

4

Hãy thử phương pháp mở rộng này. Hy vọng điều này có thể giúp đỡ.

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

Sử dụng:

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

Một cách giải quyết khác, không đẹp mua khả thi.

Tôi có một tệp XML có một phần tử gọi là "MEMDES" với hai thuộc tính là "GRADE" và "SPD" để ghi thông tin mô-đun RAM. Có rất nhiều mặt hàng song công trong SPD.

Vì vậy, đây là mã tôi sử dụng để loại bỏ các mục bị sao chép:

        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

Khi bạn không muốn viết IEqualityComparer, bạn có thể thử một cái gì đó như sau.

 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; }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.