Lọc các bộ sưu tập trong C #


142

Tôi đang tìm kiếm một cách rất nhanh để lọc ra một bộ sưu tập trong C #. Tôi hiện đang sử dụng các bộ sưu tập Danh sách <object> chung, nhưng sẵn sàng sử dụng các cấu trúc khác nếu chúng hoạt động tốt hơn.

Hiện tại, tôi chỉ đang tạo một Danh sách mới <object> và lặp qua danh sách ban đầu. Nếu tiêu chí lọc phù hợp, tôi đặt một bản sao vào danh sách mới.

Có cách nào tốt hơn để làm điều này? Có cách nào để lọc tại chỗ để không có danh sách tạm thời cần thiết không?


Điều đó sẽ được nhanh chóng rực rỡ. Có phải nó làm cho hệ thống của bạn chậm lại? Là một danh sách lớn ? Nếu không, tôi sẽ không lo lắng.
Người giữ Iain

Câu trả lời:


237

Nếu bạn đang sử dụng C # 3.0, bạn có thể sử dụng linq, cách tốt hơn và thanh lịch hơn:

List<int> myList = GetListOfIntsFromSomewhere();

// This will filter out the list of ints that are > than 7, Where returns an
// IEnumerable<T> so a call to ToList is required to convert back to a List<T>.
List<int> filteredList = myList.Where( x => x > 7).ToList();

Nếu bạn không thể tìm thấy .Where, điều đó có nghĩa là bạn cần nhập using System.Linq;ở đầu tệp của mình.


19
Phương thức tiện ích mở rộng trả về IEnumerable <T>, không phải List <T>. Nó phải là: myList.Where (x => x> 7) .ToList ()
Rafa Castaneda

1
Làm thế nào để làm việc này để lọc theo chuỗi. Giống như tìm tất cả các mục trong danh sách các chuỗi bắt đầu bằng "ch"
joncodo

2
@JonathanO Bạn có thể sử dụng các phương thức bên trong Func. listOfStrings.Where (s => s.StartsWith ("ch")). ToList ();
Mike G

1
Có cách nào để đối tượng hóa truy vấn linq? Ví dụ, để sử dụng .Where(predefinedQuery)thay vì sử dụng .Where(x => x > 7)?
XenoRo

2
@AlmightyR: Chỉ cần định nghĩa nó là một phương thức có một đối số. Vd : public bool predefinedQuery(int x) { return x > 7; }. Sau đó, bạn .Where(predefinedQuery)sẽ làm việc tốt.
Don

21

Dưới đây là một khối mã / ví dụ về một số lọc danh sách bằng ba phương thức khác nhau mà tôi kết hợp để hiển thị lọc danh sách dựa trên Lambdas và LINQ.

#region List Filtering

static void Main(string[] args)
{
    ListFiltering();
    Console.ReadLine();
}

private static void ListFiltering()
{
    var PersonList = new List<Person>();

    PersonList.Add(new Person() { Age = 23, Name = "Jon", Gender = "M" }); //Non-Constructor Object Property Initialization
    PersonList.Add(new Person() { Age = 24, Name = "Jack", Gender = "M" });
    PersonList.Add(new Person() { Age = 29, Name = "Billy", Gender = "M" });

    PersonList.Add(new Person() { Age = 33, Name = "Bob", Gender = "M" });
    PersonList.Add(new Person() { Age = 45, Name = "Frank", Gender = "M" });

    PersonList.Add(new Person() { Age = 24, Name = "Anna", Gender = "F" });
    PersonList.Add(new Person() { Age = 29, Name = "Sue", Gender = "F" });
    PersonList.Add(new Person() { Age = 35, Name = "Sally", Gender = "F" });
    PersonList.Add(new Person() { Age = 36, Name = "Jane", Gender = "F" });
    PersonList.Add(new Person() { Age = 42, Name = "Jill", Gender = "F" });

    //Logic: Show me all males that are less than 30 years old.

    Console.WriteLine("");
    //Iterative Method
    Console.WriteLine("List Filter Normal Way:");
    foreach (var p in PersonList)
        if (p.Gender == "M" && p.Age < 30)
            Console.WriteLine(p.Name + " is " + p.Age);

    Console.WriteLine("");
    //Lambda Filter Method
    Console.WriteLine("List Filter Lambda Way");
    foreach (var p in PersonList.Where(p => (p.Gender == "M" && p.Age < 30))) //.Where is an extension method
        Console.WriteLine(p.Name + " is " + p.Age);

    Console.WriteLine("");
    //LINQ Query Method
    Console.WriteLine("List Filter LINQ Way:");
    foreach (var v in from p in PersonList
                      where p.Gender == "M" && p.Age < 30
                      select new { p.Name, p.Age })
        Console.WriteLine(v.Name + " is " + v.Age);
}

private class Person
{
    public Person() { }
    public int Age { get; set; }
    public string Name { get; set; }
    public string Gender { get; set; }
}

#endregion

14

List<T>có một FindAllphương thức sẽ thực hiện việc lọc cho bạn và trả về một tập hợp con của danh sách.

MSDN có một ví dụ mã tuyệt vời ở đây: http://msdn.microsoft.com/en-us/l Library / aa701359 (VS.80) .aspx

EDIT: Tôi đã viết điều này trước khi tôi hiểu rõ về LINQ và Where()phương pháp. Nếu tôi viết bài này hôm nay có lẽ tôi sẽ sử dụng phương pháp mà Jorge đề cập ở trên. Các FindAllphương pháp vẫn hoạt động nếu bạn đang mắc kẹt trong một NET 2.0 môi trường mặc dù.


4
Linq vẫn ổn, nhưng chậm hơn ít nhất một độ, vì vậy FindAll và các phương thức mở rộng (ví dụ mảng có một loạt chúng) không dựa vào IEnumerable vẫn có ý nghĩa đối với các tình huống trong đó hiệu suất hoạt động. (FWIW, tôi đã nhận được kết quả từ yếu tố 7 đến 50 thời gian cần thiết hơn bởi Linq và / hoặc IEnumerable, nói chung)
Philm

Có một lý do đây không phải là câu trả lời được chấp nhận? Nó dường như nhanh hơn và cú pháp rõ ràng hơn (không có lệnh gọi toList ()) ở cuối.
Ran Lottem

6

Bạn có thể sử dụng IEnumerable để loại bỏ sự cần thiết của danh sách tạm thời.

public IEnumerable<T> GetFilteredItems(IEnumerable<T> collection)
{
    foreach (T item in collection)
    if (Matches<T>(item))
    {
        yield return item;
    }
}

trong đó Matches là tên của phương thức lọc của bạn. Và bạn có thể sử dụng như thế này:

IEnumerable<MyType> filteredItems = GetFilteredItems(myList);
foreach (MyType item in filteredItems)
{
    // do sth with your filtered items
}

Điều này sẽ gọi hàm GetFilteredItems khi cần thiết và trong một số trường hợp bạn không sử dụng tất cả các mục trong bộ sưu tập được lọc, nó có thể cung cấp một số hiệu suất tốt.


4

Để thực hiện tại chỗ, bạn có thể sử dụng phương thức Remove ALL của lớp "Danh sách <>" cùng với lớp "Vị ngữ" tùy chỉnh ... nhưng tất cả những gì làm được đều là mã ... điều bạn là ... nhưng vâng, nó làm điều đó tại chỗ, vì vậy bạn làm cùng một danh sách tạm thời.


4

Bạn có thể sử dụng phương thức FindAll của Danh sách, cung cấp một đại biểu để lọc. Mặc dù vậy, tôi đồng ý với @ IainMH rằng không đáng để bạn lo lắng quá nhiều trừ khi đó là một danh sách lớn.


3

Nếu bạn đang sử dụng C # 3.0, bạn có thể sử dụng linq

Hoặc, nếu bạn thích, hãy sử dụng cú pháp truy vấn đặc biệt do trình biên dịch C # 3 cung cấp:

var filteredList = from x in myList
                   where x > 7
                   select x;

3

Sử dụng LINQ tương đối chậm hơn nhiều so với sử dụng một vị từ được cung cấp cho FindAllphương thức Lists . Ngoài ra, hãy cẩn thận với LINQ vì việc liệt kê listkhông thực sự được thực hiện cho đến khi bạn truy cập kết quả. Điều này có thể có nghĩa là, khi bạn nghĩ rằng bạn đã tạo một danh sách được lọc, nội dung có thể khác với những gì bạn mong đợi khi bạn thực sự đọc nó.


1

Nếu danh sách của bạn rất lớn và bạn đang lọc liên tục - bạn có thể sắp xếp danh sách gốc trên thuộc tính bộ lọc, tìm kiếm nhị phân để tìm điểm bắt đầu và điểm kết thúc.

Thời gian ban đầu O (n * log (n)) sau đó O (log (n)).

Lọc tiêu chuẩn sẽ mất O (n) mỗi lần.

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.