cách xóa các chuỗi trống khỏi danh sách, sau đó xóa các giá trị trùng lặp khỏi danh sách


81

Giả sử tôi có danh sách một số giá trị cột đến từ một bảng, làm cách nào để xóa các chuỗi trống và các giá trị trùng lặp. Vui lòng xem đoạn mã sau:

List<string> dtList = dtReportsList.AsEnumerable().Select(dr => dr.Field<string>("column1")).ToList();

Đây là những gì tôi đã viết mã vừa rồi nhưng mã của Amiram thì thanh lịch hơn, vì vậy tôi sẽ chọn câu trả lời đó ở đây là cách tôi đã làm nó:

DataTable dtReportsList = someclass.GetReportsList();

        if (dtReportsList.Rows.Count > 0)
       { 
           List<string> dtList = dtReportsList.AsEnumerable().Select(dr => dr.Field<string>("column1")).ToList();
           dtList.RemoveAll(x=>x == "");
           dtList = dtList.Distinct().ToList();         

           rcboModule.DataSource = dtList;
           rcboModule.DataBind();               
           rcboModule.Items.Insert(0, new RadComboBoxItem("All", "All"));
       }

Hiểu rằng RemoveAll () thay đổi dtList; mỗi phần tử bị xóa buộc Danh sách phải sắp xếp lại các phần tử ở các chỉ số cao hơn trong mảng cơ bản mà nó sử dụng. Sẽ nhanh hơn nếu chỉ đơn giản là bỏ qua chúng như Amiram làm với phương pháp Where của mình.
KeithS

Câu trả lời:


200
dtList  = dtList.Where(s => !string.IsNullOrWhiteSpace(s)).Distinct().ToList()

Tôi giả định chuỗi trống và khoảng trắng giống như null. Nếu không, bạn có thể sử dụng IsNullOrEmpty(cho phép khoảng trắng) hoặcs != null


Một điều nữa thôi; suy ra với Distinction () là tương đối kém hiệu quả vì phương thức phải giả định trường hợp xấu nhất.
KeithS

@KeithS Chúng tôi biết những xác nhận nào về dữ liệu Distinctnày không cho phép nó được tối ưu hóa?
Servy

Chúng tôi có thể sắp xếp danh sách và sau đó khẳng định rằng nó đã được sắp xếp, làm cho thuật toán giảm tải tuyến tính; xem câu trả lời của tôi.
KeithS

9

Câu trả lời của Amiram là đúng, nhưng Distinction () khi được triển khai là một phép toán N 2 ; đối với mỗi mục trong danh sách, thuật toán so sánh nó với tất cả các phần tử đã được xử lý và trả về nó nếu nó là duy nhất hoặc bỏ qua nó nếu không. Chúng ta có thể làm tốt hơn.

Một danh sách đã sắp xếp có thể được sao lưu theo thời gian tuyến tính; nếu phần tử hiện tại bằng phần tử trước đó, hãy bỏ qua nó, nếu không thì trả về nó. Sắp xếp là NlogN, vì vậy ngay cả khi phải sắp xếp bộ sưu tập, chúng tôi vẫn nhận được một số lợi ích:

public static IEnumerable<T> SortAndDedupe<T>(this IEnumerable<T> input)
{
   var toDedupe = input.OrderBy(x=>x);

   T prev;
   foreach(var element in toDedupe)
   {
      if(element == prev) continue;

      yield return element;
      prev = element;      
   }
}

//Usage
dtList  = dtList.Where(s => !string.IsNullOrWhitespace(s)).SortAndDedupe().ToList();

Điều này trả về các phần tử giống nhau; chúng chỉ được sắp xếp.


Tuyệt quá. Nếu tôi không sai, bằng cách lặp lại các phần tử mà bạn thực sự đang thực hiện thứ tự. Bạn có thể nghĩ ra cách làm cho phương pháp của mình trở nên "lười biếng" không?
Amiram Korach

Thật không may, hầu hết các loại yêu cầu kiến ​​thức về toàn bộ bộ sưu tập để được sắp xếp; phần tử cuối cùng có thể là phần tử đầu tiên cần được trả lại. Vì vậy, tất cả các phần tử của đầu vào phải được đánh giá để tạo ra phần tử đầu tiên của đầu ra. Kiểu duy nhất mà tôi có thể nghĩ đến là có thể bị gián đoạn sau khi tìm thấy phần tử tiếp theo trong đầu ra của nó là một biến thể SelectionSort, và trong trường hợp đó, chúng tôi quay lại nơi chúng tôi bắt đầu.
KeithS

Bên cạnh đó, trong trường hợp của chúng tôi, kết quả của toàn bộ hoạt động là một danh sách, yêu cầu bắt đầu thực hiện "háo hức". Nếu chúng ta muốn làm việc với nó như một IEnumerable và trì hoãn việc thực thi nó, bạn có thể lấy phần thịt của hàm và đặt nó vào một lớp Iterator ẩn có triển khai IEnumerable.
KeithS

Distinctsử dụng băm và phải gần O (N) hơn O (N ^ 2). nguồn
Risky Martin

... Vâng, tôi sẽ yêu, nó thực sự; System.Linq.Set là một triển khai bảng băm nội bộ được sử dụng bởi Distinction, sẽ gần thời gian truy cập O (1) giả sử việc triển khai GetHashCode () cho các mục của bạn là hiệu quả và tạo ra một hàm băm phân phối đều (triển khai mặc định sẽ làm như vậy) . Tuy nhiên, bảng băm có vấn đề về bộ nhớ; Việc triển khai cơ bản của .NET sử dụng hai mảng, một trong số các int và một mảng khác của các mục được liên kết, mỗi mảng tốt nhất bằng số lượng mục trong tập hợp và tệ nhất là gấp đôi.
KeithS

1

Giải pháp Amiram Korach thực sự gọn gàng. Đây là một giải pháp thay thế vì mục đích linh hoạt.

var count = dtList.Count;
// Perform a reverse tracking.
for (var i = count - 1; i > -1; i--)
{
    if (dtList[i]==string.Empty) dtList.RemoveAt(i);
}
// Keep only the unique list items.
dtList = dtList.Distinct().ToList();

4
Mặc dù điều này sẽ hoạt động, nhưng mệnh đề Where nhanh hơn vì nó không phải thay đổi tập hợp đầu vào. Bạn đang giảm thiểu số lượng "thay đổi" phải được thực hiện khi xóa các phần tử khỏi danh sách, nhưng Where không xóa bất kỳ thứ gì khỏi đầu vào; nó chỉ bỏ qua các phần tử không khớp.
KeithS

0

Để đơn giản hóa giải pháp của Amiram Korach :

dtList.RemoveAll(s => string.IsNullOrWhiteSpace(s))

Không cần sử dụng Distinction () hoặc ToList ()

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.