กรองคอลเลกชันใน C #


142

ฉันกำลังมองหาวิธีที่รวดเร็วในการกรองคอลเล็กชันใน C # ขณะนี้ฉันใช้คอลเลกชัน List <object> ทั่วไป แต่เปิดให้ใช้โครงสร้างอื่นได้

ขณะนี้ฉันเพิ่งสร้างรายการ <object> ใหม่และวนลูปผ่านรายการเดิม หากเกณฑ์การกรองตรงกับฉันใส่สำเนาลงในรายการใหม่

มีวิธีที่ดีกว่าในการทำเช่นนี้? มีวิธีการกรองในสถานที่จึงไม่จำเป็นต้องมีรายการชั่วคราวหรือไม่?


นั่นกำลังจะเกิดขึ้นอย่างรวดเร็ว มันทำให้ระบบของคุณช้าหรือไม่? เป็นรายการใหญ่หรือไม่ มิฉะนั้นฉันจะไม่ต้องกังวล
ผู้ถือเลน

คำตอบ:


237

หากคุณใช้ C # 3.0 คุณสามารถใช้ linq ได้ดีขึ้นและสวยงามยิ่งขึ้น:

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

หากคุณไม่พบ.Whereนั่นหมายความว่าคุณต้องนำเข้าusing System.Linq;ที่ด้านบนของไฟล์ของคุณ


19
กระบวนการส่วนขยาย Where คืนค่า IEnumerable <T> ไม่ใช่ List <T> มันควรจะเป็น: myList.Where (x => x> 7) .ToList ()
Rafa Castaneda

1
มันทำงานอย่างไรสำหรับการกรองตามสตริง เช่นการค้นหารายการทั้งหมดในรายการสตริงที่ขึ้นต้นด้วย "ch"
joncodo

2
@JonathanO คุณสามารถใช้วิธีการภายใน Func listOfStrings.Where (s => s.StartsWith ("ch")) ToList ();
Mike G

1
มีวิธีคัดค้านการสืบค้น linq หรือไม่? ตัวอย่างเช่นการใช้.Where(predefinedQuery)แทนการใช้.Where(x => x > 7)?
XenoRo

2
@AlmightyR: เพียงกำหนดเป็นวิธีการหนึ่งที่โต้แย้ง public bool predefinedQuery(int x) { return x > 7; }Ex: จากนั้นคุณ.Where(predefinedQuery)จะทำงานได้ดี
อย่า

21

นี่คือบล็อกรหัส / ตัวอย่างของการกรองรายการโดยใช้สามวิธีที่แตกต่างกันที่ฉันรวบรวมเข้าด้วยกันเพื่อแสดงการกรองตามรายการ Lambdas และ 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>มีFindAllวิธีที่จะทำการกรองสำหรับคุณและส่งกลับชุดย่อยของรายการ

MSDN มีตัวอย่างโค้ดที่ยอดเยี่ยมที่นี่: http://msdn.microsoft.com/en-us/library/aa701359(VS.80).aspx

แก้ไข: ฉันเขียนสิ่งนี้ก่อนที่ฉันจะมีความเข้าใจที่ดีเกี่ยวกับ LINQ และWhere()วิธีการ ถ้าฉันจะเขียนวันนี้ฉันอาจจะใช้วิธีการที่ Jorge กล่าวถึงข้างต้น FindAllวิธีการยังคงทำงานถ้าคุณกำลังติดอยู่ในสภาพแวดล้อม NET 2.0 แม้ว่า


4
Linq นั้นใช้ได้ แต่อย่างน้อยหนึ่งขนาดก็ช้าลงดังนั้น FindAll และวิธีการกรองส่วนขยาย (ตัวอย่างเช่นอาเรย์จะมีกลุ่มของมันมากมาย) ซึ่งไม่ต้องพึ่งพา IEnumerable (FWIW ฉันได้รับผลลัพธ์จากปัจจัย 7 ถึง 50 เวลาที่ Linq และ / หรือ IEnumerable ต้องการโดยทั่วไป)
Philm

มีเหตุผลนี้ไม่ใช่คำตอบที่ยอมรับหรือไม่? ดูเหมือนว่าจะเร็วกว่าและการเรียกใช้ไวยากรณ์จะชัดเจนกว่า (ไม่มี toList ()) ในตอนท้าย
Ran Lottem

6

คุณสามารถใช้ IEnumerable เพื่อกำจัดความต้องการของรายการชั่วคราว

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

โดยที่ Matches คือชื่อของวิธีการกรองของคุณ และคุณสามารถใช้สิ่งนี้เช่น:

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

สิ่งนี้จะเรียกใช้ฟังก์ชัน GetFilteredItems เมื่อจำเป็นและในบางกรณีที่คุณไม่ได้ใช้รายการทั้งหมดในคอลเลกชันที่ถูกกรองอาจทำให้ได้รับประสิทธิภาพที่ดี


4

คุณสามารถใช้เมธอด RemoveAll ของคลาส "List <>" พร้อมกับคลาส "เพรดิเคต" ที่กำหนดเอง ... แต่สิ่งที่ทำความสะอาดโค้ด ... ภายใต้ประทุนทำแบบเดียวกัน สิ่งที่คุณเป็น ... แต่ใช่มันทำในสถานที่ดังนั้นคุณทำเช่นเดียวกันรายการอุณหภูมิ


4

คุณสามารถใช้วิธีการFindAllของรายการโดยการมอบสิทธิ์ให้ตัวแทนในการกรอง แม้ว่าฉันเห็นด้วยกับ @ IainMHว่ามันไม่คุ้มค่าที่จะกังวลตัวเองมากเกินไปเว้นแต่จะเป็นรายการใหญ่


3

หากคุณใช้ C # 3.0 คุณสามารถใช้ linq

หรือหากคุณต้องการให้ใช้ไวยากรณ์คิวรีพิเศษที่คอมไพเลอร์ C # 3 มีให้:

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

3

การใช้ LINQ ค่อนข้างช้ากว่าการใช้เพรดิเคตที่จัดหาให้กับFindAllเมธอดรายการ ระวังด้วย LINQ เนื่องจากการแจงนับของlistจะไม่ถูกดำเนินการจริง ๆ จนกว่าคุณจะเข้าถึงผลลัพธ์ นี่อาจหมายความว่าเมื่อคุณคิดว่าคุณสร้างรายการที่กรองแล้วเนื้อหาอาจแตกต่างจากที่คุณคาดไว้เมื่อคุณอ่าน


1

หากรายการของคุณมีขนาดใหญ่มากและคุณกรองซ้ำหลายครั้ง - คุณสามารถเรียงลำดับรายการดั้งเดิมบนแอตทริบิวต์ตัวกรองการค้นหาแบบไบนารีเพื่อค้นหาจุดเริ่มต้นและจุดสิ้นสุด

เวลาเริ่มต้น O (n * log (n)) จากนั้น O (log (n))

ตัวกรองมาตรฐานจะใช้ O (n) ทุกครั้ง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.