2013-06-06 55 views
32

Trong C#, tôi có thể sử dụng một cái gì đó như:Kiểm tra một danh sách với giá trị null cho bản sao trong C#

List<string> myList = new List<string>(); 

if (myList.Count != myList.Distinct().Count()) 
{ 
    // there are duplicates 
} 

để kiểm tra các yếu tố trùng lặp trong một danh sách. Tuy nhiên, khi có null mục trong danh sách, điều này tạo ra kết quả dương tính giả. Tôi có thể làm điều này bằng cách sử dụng một số mã chậm chạp nhưng là có một cách để kiểm tra các bản sao trong một danh sách trong khi bỏ qua các giá trị null với một cách súc tích?

Trả lời

32

Tôi muốn làm điều này khác nhau:

Với tuyên bố LINQ sẽ được đánh giá một cách lười biếng, các .Any sẽ ngắn mạch - có nghĩa là bạn không phải lặp lại & đếm toàn bộ danh sách, nếu có trùng lặp - và như vậy, sẽ hiệu quả hơn.

var dupes = myList 
    .Where(item => item != null) 
    .GroupBy(item => item) 
    .Any(g => g.Count() > 1); 

if(dupes) 
{ 
    //there are duplicates 
} 

EDIT: http://pastebin.com/b9reVaJu Một số LINQPad điểm chuẩn mà dường như để kết luận GroupBy với Count() nhanh

EDIT 2: Câu trả lời của Rawling dưới đây dường như ít nhất 5x nhanh hơn so với phương pháp này!

+0

+1 Tôi thích câu trả lời này hơn, có vẻ ngắn gọn hơn đối với tôi. –

+5

'Bất kỳ' vẫn yêu cầu toàn bộ đầu vào được lặp lại ít nhất một lần (để xây dựng nhóm), và một phần của nó được lặp lại lần thứ hai (để kiểm tra độ dài của các nhóm). shortcircuit 'Count' mà không lặp lại) - trong khi nó có thể thực hiện kiểm tra này với * nhiều nhất * một lần lặp của toàn bộ đầu vào. – Rawling

+0

@Rawling Tôi giả định vì Count() được đánh giá là một phần của 'Bất kỳ' - nó sẽ đánh giá nhóm lười biếng - làm thế nào chúng ta có thể kiểm tra điều này? Khá thú vị! –

11
var nonNulls = myList.Where(x => x != null) 
if (nonNulls.Count() != nonNulls.Distinct().Count()) 
{ 
    // there are duplicates 
} 
+1

Đây có lẽ là giải pháp dễ đọc nhất. – svick

+1

điều này lặp lại danh sách hai lần mặc dù –

4

Vâng, hai null là trùng lặp, phải không?

Dù sao, so sánh danh sách mà không null:

var denullified = myList.Where(l => l != null); 
if(denullified.Count() != denullified.Distinct().Count()) ... 
+3

Tôi có thể nghĩ về rất nhiều tình huống không xem xét nulls trùng lặp. Chúng tôi có một trang web nơi người dùng có thể đăng nhập qua email hoặc tên người dùng (tùy chọn). Người dùng không có tên người dùng có null cho tên người dùng, nhưng không có tên người dùng nào có thể được sao chép. – xdumaine

1

EDIT lần thử đầu tiên của tôi bị hút vì không được hoãn lại. thực hiện

thay vào đó,

var duplicates = myList 
    .Where(item => item != null) 
    .GroupBy(item => item) 
    .Any(g => g.Skip(1).Any()); 

nghèo xóa.

+1

Bạn có nghĩa là 'ToLookup()'? – svick

+0

@svick tất nhiên. Tuy nhiên, – Jodrell

+0

@svick, điều đó không đủ lười. – Jodrell

55

Nếu bạn lo lắng về hiệu suất, mã sau sẽ dừng ngay khi tìm thấy mục trùng lặp đầu tiên - tất cả các giải pháp khác cho đến nay yêu cầu toàn bộ đầu vào phải được lặp lại ít nhất một lần.

var hashset = new HashSet<string>(); 
if (myList.Where(s => s != null).Any(s => !hashset.Add(s))) 
{ 
    // there are duplicates 
} 

hashset.Add lợi nhuận false nếu mục đã tồn tại trong các thiết lập, và Any lợi nhuận true càng sớm càng giá trị đầu tiên true xảy ra, vì vậy đây sẽ chỉ tìm kiếm đầu vào như xa như bản sao đầu tiên.

+0

Tôi nghĩ 'GroupBy' đã bị trì hoãn? – Jodrell

+2

@Jodrell Nó được hoãn lại cho đến khi bạn cố gắng đọc nhóm đầu tiên, tại thời điểm đó nó đọc toàn bộ chuỗi đầu vào, nhóm nó, và trả về nhóm đầu tiên. – Rawling

+0

trong trường hợp đó, yêu cầu 'HashSet'. – Jodrell

Các vấn đề liên quan