เริ่มเรียนรู้ Collection (ตอนที่ 3/4) – Collection กับ Lambda Expression

โพสนี้เป็นตอนที่ 3 ของซีรีย์ Collection ครับ ใครยังไม่ได้อ่านตอนที่ 1 และตอนที่ 2 ก็คลิกกลับไปอ่านกันก่อนที่จะงง
หัวข้อนี้ผมจะนำ Lambda Expression มาประยุกต์ใช้ร่วมกับ Collection เพื่อยกตัวอย่างการจัดเรียง (Sort) หรือการค้นหา (Search) ข้อมูลใน Collection ครับ

ในตอนที่ 2ผมมีเกริ่นๆ ไว้แล้วว่าโพสนี้ผมจะไม่อธิบาย Lambda Expression แบบถึงแก่น เพราะจะยกยอดไปพูดในซีรี่ย์ Delegate ดังนั้น Lambda Expression ในโพสนี้จะเป็นเวอร์ชั่นเรียนลัดสุดๆ ครับ

เรียงข้อมูลด้วย Lambda expression
Lambda expression อธิบายให้คนทั่วไปเข้าใจได้ง่ายๆ ว่า เป็นการเขียนโปรแกรมรูปแบบหนึ่ง แบบใหม่ล่าสุดที่มีมาตั้งแต่ C# 3.0 เพื่อให้เราสามารถสร้างกฏเกณฑ์ให้กับการจัดเรียงข้อมูลด้วยเมธอด Sort() ได้ หากเข้าใจในหัวข้อนี้แล้วคุณผู้อ่านจะสามารถสร้าง “กฏ” เพื่อนำไปใช้ในการเขียนโปรแกรมรูปแบบอื่นๆ ได้อีกหลากหลาย ไม่จำกัดแค่เพียงการจัดเรียงข้อมูล ถ้าสนใจก็คลิกไปอ่านซีรี่ย์ Delegate เพิ่มเติมได้นะครับ

ผมจะติวแบบรวบรัด ถึงวิธีการสร้าง “กฏ” การจัดเรียงข้อมูลด้วย Lambda expression มาเริ่มกันเลย เริ่มจากเราจะตั้งกฏว่า

“อินสแทนซ์สองตัวใน List<Customer> จะเรียงกันแบบน้อยไปมาก โดยเทียบตามพรอเพอร์ตีส์ Age”

กฏชัดเจนดีนะครับ วิธีการสร้างกฏที่ว่าก็เริ่มจาก การเรียงข้อมูลแต่ละครั้ง เราต้องเอาอินสแทนซ์สองตัวมาเปรียบเทียบกัน ตัวไหนน้อยกว่า ก็จะอยู่หน้า ตัวไหนมากกว่า ก็ต้องไปต่อท้าย

ดังนั้น “กฏ” ของเราจะเริ่มจากการระบุอินสแทนซ์สองตัวที่จะนำมาเปรียบเทียบกันก่อน ใช้ชื่ออะไรก็ได้ครับ ผมใช้ cus1 กับ cus2

        (cus1, cus2)

หลังจากระบุอินสแทนซ์สองตัวลอยๆ ขึ้นมาแล้ว ใส่ Lambda expression ต่อท้ายเข้าไป สัญลักษณ์ของ Lambda expression ใน C# ก็คือ =>

        (cus1, cus2) =>

หลังจากนั้น เราจะเอาเจ้า cus1 และ cus2 มาเปรียบเทียบกันอย่างไร ก็บอกวิธีการไป รู้จักเมธอด CompareTo() มาจากในซีรี่ย์ Generic แล้วนะครับ ผมจะเอามาใช้ประโยชน์ โดยการนำอายุ (Age) ของ cus1 และ cus2 มาเปรียบเทียบกัน (Age เป็นข้อมูลประเภท int หรือ System.Int32 ซึ่งมีการแปะ interface IComparable เอาไว้แล้ว เราจึงสามารถเรียกเมธอด CompareTo() ได้เลยโดยไม่ต้องแปะ interface ด้วยตนเอง)

        (cus1, cus2) => cus1.Age.CompareTo(cus2.Age));

เสร็จแล้วครับ กฏเกณฑ์การเปรียบเทียบของเรา เรียนรู้ด้วยหลักสูตรรวบรัด จับทั้งบรรทัดนี้ ยัดเข้าไปในเมธอด Sort() ได้เลยครับ จะได้โปรแกรมนี้

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    using System;
    using System.Collections.Generic;
 
    namespace ApricotAcademy.Blog.Example
    {
        class ListSortExample
        {
            static void Main(string[] args)
            {
                List<Customer> list = new List<Customer>();
 
                list.Add(new Customer("Ronaldo", 26));
                list.Add(new Customer("Berbatov", 30));
                list.Add(new Customer("Torres", 27));
 
                list.Sort((cus1, cus2) => cus1.Age.CompareTo(cus2.Age));
 
                foreach (Customer cus in list)
                    Console.WriteLine(cus.Name + " : " + cus.Age);
 
                Console.ReadLine();
            }
        }
    }

ดูที่บรรทัด 16 นะครับ เมธอด Sort() ผมใส่ Lambda expression เข้าไป แค่นี้ก็เรียบร้อยครับ การจัดเรียงข้อมูล ง่ายมั้ยครับ อันที่จริงหัวข้อสั้นๆ นี้ใช้ความรู้ถึงสามเรื่องด้วยกันครับ ได้แก่เรื่องดีลิเกต (delegate), เมธอดแบบ anonymous และ lambda expression

หมายเหตุ
อัลกอริทึ่มที่ใช้ในการจัดเรียงข้อมูลของเมธอด Sort() ของคอลเลกชั่น (ส่วนใหญ่) นั้นเรียกว่า quick sort ครับ ซึ่งเป็นการเรียงข้อมูลที่มีประสิทธิภาพอันดับต้นๆ ของโลกเลยทีเดียว ดูข้อมูลของ quick sort ได้ที่ http://lab.ai/quicksort

ค้นหาข้อมูล (find) ด้วย Lambda expression
ทีนี้ถ้าคุณรู้จัก Lambda expression ฉบับรวบรัดไปแล้ว คุณจะสามารถเข้าใจหัวข้อนี้ได้อย่างรวดเร็วครับ เช่นเดียวกับการจัดเรียงข้อมูล การค้นหาข้อมูล เราก็นำ Lambda expression มาใช้เช่นกัน ด้วย “กฏ” ที่ผมตั้งขึ้นมาเองว่า

ค้นหาอินสแทนซ์ใน List<Customer> เฉพาะ Customer ที่มีอายุน้อยกว่า 28 ปี”

ตีความออกมาเป็น Lambda expression ได้ง่ายๆ แบบนี้ครับ

        (cus) => cus.Age < 28

จับมาใส่ในเมธอด FindAll() จะได้โค้ดโปรแกรมดังตัวอย่างด้านล่าง

        List<Customer> young = list.FindAll((cus) => cus.Age < 28);

ไปดูโปรแกรมเต็มๆ กันเลยครับ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    using System;
    using System.Collections.Generic;
 
    namespace ApricotAcademy.Blog.Example
    {
        class ListFindExample
        {
            static void Main(string[] args)
            {
                List<Customer> list = new List<Customer>();
 
                list.Add(new Customer("Ronaldo", 26));
                list.Add(new Customer("Berbatov", 30));
                list.Add(new Customer("Torres", 27));
 
                List<Customer> young = list.FindAll((cus) => cus.Age < 28);
 
                foreach (Customer cus in young)
                    Console.WriteLine(cus.Name + " : " + cus.Age);
 
                Console.ReadLine();
            }
        }
    }

ผลลัพธ์ของโปรแกรมจะได้ Customer เพียงแค่สองคนภายในคอลเลกชั่น young ก็คือโรนัลโด้ (Ronaldo) และตอเรส (Torres) ส่วนเบอบาตอฟ (Berbatov) ซึ่งอายุมากแล้วไม่ตรงกับ “กฏ” ที่เราตั้งใน Lambda expression จึงถูกตัดออกไป

นอกจากการค้นหาข้อมูลด้วยเมธอด FindAll() ของคอลเลกชั่นแล้ว ยังมีวิธีการค้นหาข้อมูลระดับสุดยอดอีกรูปแบบหนึ่ง ซึ่งผมจะกล่าวถึงในโพสเกี่ยวกับ LINQ ปูพื้นฐานโพสนี้ให้แน่นๆ แล้วเตรียมพร้อมไปลุยกับ LINQ ได้เลยครับ **เมื่อเขียนซีรีย์ LINQ เรียบร้อยเมื่อไหร่จะแจ้งอีกครั้งครับ

หมายเหตุ
เรื่องของ Lambda expression เป็นเทคนิคใหม่ที่มีในภาษา C# 3.0 สำหรับคุณผู้อ่านท่านใดที่ยังเขียน C# 2.0 อยู่จะไม่สามารถเขียนโปรแกรมด้วย Lambda expression ได้นะครับ จำเป็นต้องใช้วิธีการเขียนโปรแกรมที่เรียกว่า Delegate ครับ

จบตอนที่ 3 ของคอลเลกชั่นครับ คิดว่าน่าจะทำให้เข้าใจภาพของการใช้งานคอลเลกชั่นมากขึ้น ส่วนในตอนสุดท้ายคือตอนที่ 4 ผมจะยกตัวอย่างคอลเลกชั่นประเภทต่างๆ ที่จะเจอะเจอกันบ่อยๆ ใน .NET มายกตัวอย่างให้พอคุ้นตากันนะครับ