ความเดิมตอนที่แล้ว… ผู้เขียนได้ทิ้งท้ายไว้เกี่ยวกับเรื่องการใช้งาน LINQ ในการจัดการข้อมูลในเบื้องต้น ได้แก่ วิธีการดึงข้อมูลโดยทั่วไป(Select) การดึงข้อมูลแบบมีเงื่อนไข(Where) และการเรียงลำดับ(OrderBy) เป็นต้น หากใครที่ยังไม่เคยอ่านบทความที่แล้ว และต้องการศึกษาในส่วนดังกล่าวสามารถหาอ่านได้จากลิงค์ “การใช้ LINQ ในการจัดการข้อมูลอย่างง่าย สำหรับมือใหม่(Ep.1)” เพื่อเพิ่มความเข้าใจพื้นฐานในการใช้งานเบื้องต้น LINQ เพิ่มเติม และสำหรับในบทความนี้ ผู้เขียนจะขอพูดถึงการใช้งาน LINQ ในส่วนอื่นๆที่นอกเหนือจากการทำงานทั่วไป ที่คิดว่าน่าจะเป็นประโยชน์กับผู้พัฒนาที่มีความสนใจในการใช้งาน LINQ จัดการข้อมูล ดังนี้
- การคำนวณค่าร่วม/นับจำนวน
ตัวอย่างที่ 1 : การคำนวณค่าผลรวมของฟิลด์ที่ดึงข้อมูลโดยใช้เมธอด Sum
decimal sumLineTotal = (from od in orderdetailscollection select od.LineTotal).Sum();
หรือ
decimal sumLineTotal = orderdetailscollection.Sum(od => od.LineTotal);
คำอธิบาย : จากตัวอย่างข้างต้น เป็นการดึงข้อมูลโดยมีการคำนวณค่าผลรวมที่ได้จากการดึงข้อมูลทั้งหมดในฟิลด์ LineTotal ผ่านเมธอด Sum โดยที่ไม่ต้องมาวนค่าเพื่อหาผลรวมของแต่ละฟิลด์ที่ดึงมาอีกครั้งในภายหลัง ซึ่งถือว่าเป็นการอำนวยความสะดวกและประหยัดเวลาในการพัฒนาให้กับผู้พัฒนาที่ต้องการทำงานในกรณีดังกล่าวได้
ตัวอย่างที่ 2 : เป็นการคำนวณค่าเฉลี่ยตามรหัส
double RatingAverage = ctx.Rates.Where(r => r.Id == Id).Average(r => r.Rating);
หรือ
var RatingAverage = (from a in ctx.Rates where a. Id.Equals(id) select a.Rating).Average();
คำอธิบาย : จากตัวอย่างข้างต้น เป็นการคำนวณหาค่าเฉลี่ยของฟิลด์ Rating โดยใช้เมธอด Average ภายใต้เงื่อนไขรหัส Id ในการดึงข้อมูล
ตัวอย่างที่ 3 : เป็นตัวอย่างการคำนวณผลรวม และการนับจำนวนแถวของการอ่านข้อมูลโดยมีการจัดกลุ่มข้อมูลร่วมด้วย
var ListByOwner = list.GroupBy(l => l.Owner) .Select(lg => new { Owner = lg.Key, Boxes = lg.Count(), TotalWeight = lg.Sum(w => w.Weight), AverageVolume = lg.Average(w => w.Volume) });
คำอธิบาย : จากตัวอย่างข้างต้น จะเห็นได้ว่าเป็นการจัดกลุ่มของข้อมูลตาม Owner โดยใช้เมธอด GroupBy และมีการนับจำนวนแถวเก็บไว้ในฟิลด์ Boxes คำนวณผลรวมของคอลัมน์ Weight และหาค่าเฉลี่ยของคอลัมน์ Volume ใส่ในฟิลด์ TotalWeight และ AverageVolume นั่นเอง
ตัวอย่างที่ 4 : เป็นการนับจำนวนข้อมูลตามเงื่อนไขที่กำหนดโดยใช้เมธอด Count
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; int oddNumbers = numbers.Count(n => n % 2 == 1);
คำอธิบาย : จากตัวอย่างข้างต้น จะเห็นว่า เป็นการดึงค่าจากตัวแปรอาร์เรย์ที่ชื่อว่า numbers และนับจำนวนตัวเลขเฉพาะที่เป็นเลขคี่(หารด้วยสองและมีค่าเศษ 1)เท่านั้นด้วยเมธอด Count ซึ่งในส่วนของเงื่อนไขดังกล่าว ผู้อ่านสามารถนำไปประยุกต์ใช้กับงานของตนในการนับจำนวนข้อมูลได้
ผลลัพธ์ : จากการอ่านข้อมูลจากตัวแปร numbers ส่งผลให้ oddNumbers มีค่าเท่ากับ 5 นั่นคือในการดึงข้อมูลพบเลขที่มีค่าเป็นเลขคี่ดังนี้ 5,1,3,9,7
- การค่าที่น้อยที่สุดและมากที่สุดโดยใช้เมธอด Min/Max
ตัวอย่างที่ 1 : การหาค่าที่น้อยที่สุดโดยใช้เมธอด Min
string[] words = { "cherry", "apple", "blueberry" }; int shortestWord = words.Min(w => w.Length);
คำอธิบาย : จากตัวอย่างข้างต้น จะเห็นว่า เป็นการอ่านค่าจากตัวแปรอาร์เรย์ที่ชื่อว่า words และหาค่าที่น้อยที่สุดของจำนวนความยาวตัวอักษรจากค่าที่อ่านได้ด้วยเมธอด Min
ผลลัพธ์ : shortestWord มีค่าเท่ากับ 5 ซึ่งก็คือค่าความยาวตัวอักษรของคำว่า “apple” ที่มีค่าเท่ากับ 5 ซึ่งเป็นค่าที่น้อยที่สุดนั่นเอง
ตัวอย่างที่ 2 : เป็นการหาราคาที่ถูกที่สุดจากรายการสินค้าที่อ่านได้แต่ละประเภท(จัดกลุ่มตามประเภทสินค้า) โดยใช้เมธอด Min
List<Product> products = GetProductList(); var categories = from p in products group p by p.Category into g select new { Category = g.Key, CheapestPrice = g.Min(p => p.UnitPrice) };
คำอธิบาย : จากตัวอย่างข้างต้น เป็นการหาค่าที่น้อยที่สุดของราคาในคอลัมน์ UnitPrice จาก list ของคลาส Product ที่ดึงมาได้ และมีการจัดกลุ่มตามประเภทของสินค้า นั่นหมายถึง ค่าที่ได้จากการอ่านข้อมูลนี้จะเป็นค่าของราคาสินค้าต่อหน่วยที่น้อยที่สุดในแต่ละประเภทสินค้านั่นเอง
หมายเหตุ : การใช้งานเมธอด Max จะมีลักษณะการทำงานเช่นเดียวกับเมธอด Min แต่เป็นการหาค่าที่มากที่สุดจากการอ่านข้อมูล ซึ่งผู้อ่านสามารถนำไปใช้แทนกันได้ตามตัวอย่างการใช้งานเมธอด Min ข้างต้นได้
- การใช้งาน set operator
- การกำจัดข้อมูลที่ซ้ำกัน
ตัวอย่างที่ 1 : เป็นการจำกัดข้อมูลที่ซ้ำกันด้วยเมธอด Distinct/DistinctBy
List<Product> products = GetProductList(); var categoryNames = ( from p in products select p.Category).Distinct();
หรือ
List<Product> products = GetProductList(); var categoryNames = products.DistinctBy(x=> x.Category);
คำอธิบาย : เป็นการดึงข้อมูลประเภทสินค้าที่ไม่ซ้ำกันไว้ในตัวแปร categoryNames โดยใช้เมธอด Distinct และ DistinctBy (ที่เรียกใช้จาก MoreLinq Library)
หมายเหตุ : หากต้องการกำจัดข้อมูลที่ซ้ำกันมากกว่า 1 คอลัมน์ สามารถทำได้ ดังนี้List<Product> products = GetProductList(); var categoryNames = products.DistinctBy(a => new { a.Category, a.Code });
ตัวอย่างที่ 2 :
List<Product> products = GetProductList(); var categoryNames = products .GroupBy(a => a.Category ) .Select(g => g.First());
คำอธิบาย : เป็นการดึงข้อมูลประเภทสินค้าที่ไม่ซ้ำกันไว้ โดยใช้เมธอดการจัดกลุ่มข้อมูลตามประเภทสินค้าด้วยเมธอด GroupBy และดึงค่าแรกสุดของแต่ละกลุ่มมาแสดงด้วยเมธอด First ตามลำดับ
ตัวอย่างที่ 3 :
var total = items.Select(item => item.Value).Distinct().Count();
คำอธิบาย : เป็นการนับจำนวนของข้อมูลที่ไม่ซ้ำกันโดยใช้เมธอด Distinct และเมธอด Count
- การผสานข้อมูลโดยใช้ Union
ตัวอย่างที่ 1 :
int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 }; int[] numbersB = { 1, 3, 5, 7, 8 }; var uniqueNumbers = numbersA.Union(numbersB);
คำอธิบาย : เป็นการรวมข้อมูลจาก 2 แหล่ง ด้วยเมธอด Union
ผลลัพธ์ : uniqueNumbers = {0, 2, 4, 5, 6, 8, 9,1, 3, 7, 8}- การเลือกเฉพาะข้อมูลที่ซ้ำกันจากข้อมูล 2 แหล่งมาแสดงด้วยเมธอด Intersect
ตัวอย่างที่ 1 :
var infoQuery = (from cust in db.Customers select cust.Country) .Intersect (from emp in db.Employees select emp.Country);
คำอธิบาย : เป็นการอ่านข้อมูลจาก 2 ส่วน คือ ในตาราง Customers และ Employees เพื่อเปรียบเทียบค่าคอลัมน์ Country จาก 2 แหล่ง หากมีซ้ำกันทั้ง 2 ที่จะดึงมาเก็บไว้ในตัวแปร infoQuery
ตัวอย่างที่ 2 :
int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 }; int[] numbersB = { 1, 3, 5, 7, 8 }; var commonNumbers = numbersA.Intersect(numbersB);
คำอธิบาย : เป็นการอ่านข้อมูลจาก 2 ส่วน คือ ในตัวแปรอาร์เรย์ numbersA และ numbersB เพื่อเปรียบเทียบค่า หากมีซ้ำกันทั้ง 2 ที่จะดึงมาเก็บไว้ในตัวแปร commonNumbers
ผลลัพธ์ : commonNumbers = { 5, 8}- การยกเว้นการอ่านข้อมูลโดยใช้ Except
ตัวอย่างที่ 1 :
List<Product> products = GetProductList(); List<Customer> customers = GetCustomerList(); var productFirstChars = from p in products select p.ProductName[0]; ///ดึงอักษรตัวแรกของชื่อสินค้า var customerFirstChars = from c in customers select c.CompanyName[0]; ///ดึงอักษรตัวแรกของชื่อบริษัท var productOnlyFirstChars = productFirstChars.Except(customerFirstChars);
คำอธิบาย : เป็นการอ่านข้อมูลจาก 2 ส่วน คือ ในส่วนของลิสต์ products และ customers โดยเอาเฉพาะอักษรตัวแรกของข้อมูลมาเพื่อเปรียบเทียบค่าโดยเทียบจาก productFirstChars หากพบว่าซ้ำกับในตัวอักษรแรกของข้อมูลชื่อบริษัทที่เก็บไว้ในตัวแปร customerFirstChars จะข้ามไป ไม่ดึงมาเก็บไว้ในตัวแปร productOnlyFirstChars แต่หากไม่ซ้ำ จะนำมาเก็บไว้ ผลลัพธ์สุดท้ายที่ได้จึงจะมีเฉพาะที่มีใน productFirstChars และไม่ซ้ำกับในข้อมูล customerFirstChars เท่านั้น
ตัวอย่างที่ 2 :int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 }; int[] numbersB = { 1, 3, 5, 7, 8 }; IEnumerable<int> aOnlyNumbers = numbersA.Except(numbersB);
คำอธิบาย : เป็นการอ่านข้อมูลจาก 2 ส่วน คือ ในตัวแปรอาร์เรย์ numbersA และ numbersB เพื่อเปรียบเทียบค่าโดยเทียบจาก numbersA เทียบกับ numbersB จะเอาเฉพาะที่มีใน numbersB แต่ไม่มีใน numbersB มาเก็บไว้ในตัวแปร aOnlyNumbers
ผลลัพธ์ : aOnlyNumbers = { 0, 2, 4, 6, 9}- การอ่านข้อมูลจาก 2 แหล่งข้อมูลด้วยวิธีการ join
- การ join แบบ innerjoin
ตัวอย่างที่ 1 : เป็นการเชื่อมตาราง 2 ตารางโดยจะเอาเฉพาะข้อมูลแถวที่มี key ร่วมกันจากทั้ง 2 แหล่ง
var result = from p in Person.BuiltPersons() join a in Address.BuiltAddresses() ///ระบุตารางที่นำมาเชื่อมกัน on p.IdAddress equals a.IdAddress ///กำหนด key ร่วมจาก 2 ตาราง select new { ///เลือกคอลัมน์ที่ต้องการนำมาใช้ Name = a.MyPerson.Name, Age = a.MyPerson.Age, PersonIdAddress = a.MyPerson.IdAddress, AddressIdAddress = a.MyAddress.IdAddress, Street = a.MyAddress.Street };
คำอธิบาย : จากตัวอย่างข้างต้น จะเห็นเป็นการเชื่อมตาราง 2 ตารางเข้าด้วยกัน และเลือกเฉพาะข้อมูลที่มีรหัส IdAddress ตรงกันมาแสดงเท่านั้น
หรือ
var resultJoint = Person.BuiltPersons().Join( /// Source Collection Address.BuiltAddresses(), /// Inner Collection p => p.IdAddress, /// กำหนด key จากตาราง Person.BuiltPersons() a => a.IdAddress, /// กำหนด key จากตาราง Address.BuiltAddresses() (p, a) => new { MyPerson = p, MyAddress = a }) /// Result Collection .Select(a => new { Name = a.MyPerson.Name, Age = a.MyPerson.Age, PersonIdAddress = a.MyPerson.IdAddress, AddressIdAddress = a.MyAddress.IdAddress, Street = a.MyAddress.Street });
- การ join แบบ leftjoin
ตัวอย่างที่ 1 :
string[] categories = new string[]{ "Beverages", "Condiments", "Vegetables", "Dairy Products", "Seafood" }; List<Product> products = GetProductList(); var q = from c in categories join p in products on c equals p.Category into ps from p in ps.DefaultIfEmpty() ///ถ้าไม่มีข้อมูลจะแสดงข้อความ (No products) select new { Category = c, ProductName = p == null ? "(No products)" : p.ProductName };
คำอธิบาย : จากตัวอย่างข้างต้น จะเห็นได้ว่าเป็นการเชื่อมข้อมูล 2 แหล่ง โดยยึดตามแหล่งข้อมูลแรก ในที่นี้คือข้อมูล categories และเทียบกับข้อมูลใน products หากข้อมูลที่เปรียบเทียบมีใน categories แต่ไม่มีใน products ก็จะนำมาแสดง โดยให้ค่าของ ProductName เป็น (No products) แทน(ตามหลักการ join แบบ leftjoin ) หรืออธิบายง่ายๆว่า จะแสดงทุกรายการที่มีใน categories นั่นเอง
- การ join แบบ rightjoin
ตัวอย่างที่ 1 :
var rightOuterJoin = from last in lastNames join first in firstNames on last.ID equals first.ID into temp from first in temp.DefaultIfEmpty(new { last.ID, Name = default(string) }) select new { last.ID, FirstName = first.Name, LastName = last.Name, };
คำอธิบาย : จากตัวอย่างข้างต้น จะเห็นได้ว่าเป็นการเชื่อมข้อมูล 2 แหล่ง โดยยึดตามแหล่งข้อมูลแรก ในที่นี้คือข้อมูล lastNames และเทียบกับข้อมูลใน firstNames โดยยึดข้อมูลจาก firstNames หากข้อมูลที่เปรียบเทียบมีใน firstNames แต่ไม่มีใน lastNames ก็จะนำมาแสดง โดยให้ค่าของ default(string) แทน(ตามหลักการ join แบบ rightjoin ) หรืออธิบายง่ายๆว่า จะแสดงทุกรายการที่มีใน firstNames นั่นเอง
จะเห็นได้ว่าจากบทความการใช้งาน LINQ ในการจัดการข้อมูลอย่างง่าย สำหรับมือใหม่ ทั้งใน Ep.1 และ Ep.2 นี้ ผู้เขียนพยายามรวบรวมวิธีที่เป็นการใช้งานพื้นฐานและยกตัวอย่างในกรณีต่างๆ เพื่อให้ผู้อ่านเห็นภาพ และทำความเข้าใจได้โดยง่ายขึ้น สามารถนำไปประยุกต์ใช้กับงานพัฒนาที่กำลังพัฒนาอยู่ได้ แต่ความสามารถของ LINQ ไม่ได้จบเพียงเท่านี้ ยังคงมีเมธอดและวิธีการใช้งานอื่นๆ รวมทั้งการประยุกต์ใช้งานอีกมากมายรอให้ผู้อ่านได้ทำการศึกษาเพิ่มเติมและหยิบมาใช้งานกัน เพื่อเพิ่มประสิทธิภาพและความคล่องตัวในการพัฒนาโปรแกรมกันต่อไป ผู้เขียนเชื่อว่าหากผู้อ่านได้มีการศึกษาและฝึกลองทำหลายๆกรณีก็จะทำให้สามารถใช้งาน LINQ ได้เกิดประโยชน์สูงสุด สุดท้ายนี้ผู้เขียนหวังเป็นอย่างยิ่งว่าสิ่งที่ผู้เขียนได้หยิบยกมาให้ผู้อ่านได้อ่านกันนี้จะเป็นประโยชน์ไม่มากก็น้อย หากผิดพลาดประการใดผู้เขียนขออภัยไว้ ณ ที่นี้ด้วย และขอบคุณที่ติดตามอ่านบทความนี้จนจบนะคะ ขอบคุณค่ะ
แหล่งข้อมูลอ้างอิง :
https://code.msdn.microsoft.com/LINQ-Aggregate-Operators-c51b3869
http://stackoverflow.com/questions/61870/sum-of-items-in-a-collection
https://code.msdn.microsoft.com/LINQ-Set-Operators-374f34fe
http://www.codeproject.com/Articles/535374/DistinctBy-in-Linq-Find-Distinct-object-by-Propert
https://code.msdn.microsoft.com/LINQ-Join-Operators-dabef4e9
http://www.codeproject.com/Articles/488643/LinQ-Extended-Joins