با سلام به کاربران عزیز با یکی دیگر از سری آموزش های LINQ در C# در خدمت شما هستیم. در این جلسه قصد داریم با اپراتورهای Partition آشنا شویم.
کاربرد این سری از اپراتور ها برای تقسیم لیست یا مجموعه به دو قسمت است که در نهایت یک قسمت به عنوان خروجی در نظر گرفته می شود که آن هم پیرو شرایط و قوانینی است که در ادامه بررسی خواهیم کرد.
اپراتورهای Partition در LINQ به چهار بخش:
Take
TakeWhile
Skip
SkipWhile
تقسیم بندی می شوند که در جدول زیر کاربرد هر یک بررسی شده است:
کاربرد | نام اپراتور |
برای بازگرداندن تعدادی مشخص از عناصر یک مجموعه یا لیست استفاده می شود | Take |
کاملا شبیه به Take است فقط عناصری که در شرطی مشخص صدق کنند را برمی گرداند | TakeWhile |
برای نادیده گرفتن تعدادی مشخص از عناصر یک مجموعه یا لیست استفاده می شود | Skip |
کاملا شبیه به Skip است، فقط عناصری که در شرطی مشخص صدق کنند را نادیده می گیرد |
SkipWhile |
فرض کنید مجموعه ای متشکل از تعدادی عضو وجود دارد و قصد شما این است که n عضو اول این مجموعه را در اختیار بگیرید، برای این کار از متد Take
استفاده می شود. کاربرد آن بسیار ساده بوده و الگوی آن به صورت زیر است:
IEnumerable<string> result = list.Take(n);
توجه کنید در اینجا نوع داده ی result
از نوع <IEnumerable<string
است. در این الگو فرض شده داده های list
از نوع string
هستند، به همین خاطر در <>
نوع string
نوشته شده است. همان طور که قبلا بیان شد برای راحتی کار می توانید از نوع داده ی var
به جای <>IEnumerable
استفاده کنید.
برای روشن تر شدن موضوع به مثال زیر توجه کنید:
using System; using System.Collections.Generic; using System.Linq; namespace LINQExamples { class Program { static void Main(string[] args) { string[] countries = { "India", "Iran", "Russia", "China", "USA", "Argentina" }; var result = countries.Take(3); foreach (string s in result) { Console.WriteLine(s); } Console.ReadLine(); } } }
در این مثال ساده یک آرایه از جنس string
با اعضایی مشخص وجود دارد و می خواهیم سه عضو اول این آرایه را در خروجی چاپ کنیم. این کار توسط فراخوانی متد Take
بر روی مجموعه ی مورد نظر قابل انجام است.
بازنویسی کد به روش Query به صورت زیر است:
var result = (from x in countries select x).Take(3);
یا
var result = from x in countries.Take(3) select x;
به مثال دوم از کاربرد Take
توجه کنید:
using System; using System.Collections.Generic; using System.Linq; namespace LINQExamples { class Program { static void Main(string[] args) { IEnumerable<Student> result = Student.GetListStu().Take(2); foreach (Student s in result) { Console.WriteLine("{0} | {1}",s.Name,s.Average); } Console.ReadLine(); } } public class Student { public int ID { get; set; } public string Name { get; set; } public Double Average { get; set; } public static List<Student> GetListStu() { return new List<Student> { new Student{ID=100,Name="Jack",Average=15.25 }, new Student{ID=101,Name="Olivia",Average=10.75 }, new Student{ID=102,Name="Freddie",Average=18.33 }, new Student{ID=103,Name="Oscar",Average=16.45 }, new Student{ID=104,Name="Emily",Average=12.25 }, }; } } }
مشاهده می کنید که در این مثال یک List
با جنس داده ی Student
با اعضایی مشخص در اختیار داریم که با استفاده از متد Take
به دو عضو اول آن دسترسی پیدا کرده ایم.
برای باز نویسی کد به روش Query به صورت زیر عمل می کنیم:
IEnumerable<Student> result = (from stu in Student.GetListStu() select stu).Take(2);
این متد تا حدودی مانند متد Take
عمل می کند فقط قابلیت پیاده سازی شرط را به همراه دارد. در واقع Take
نوع داده ی int
را به عنوان پارامتر ورودی می پذیرد اما TakeWhile
شرط را به عنوان پارامتر ورودی می گیرد.
به الگوی استفاده از آن توجه کنید:
IEnumerable<string> result = list.TakeWhile(Condition");
به مثال زیر توجه کنید:
using System; using System.Collections.Generic; using System.Linq; namespace LINQExamples { class Program { static void Main(string[] args) { string[] countries = { "India","Russia", "Iran", "China", "USA", "Argentina" }; var result = countries.TakeWhile(x =>x.StartsWith("I")); foreach (string s in result) { Console.WriteLine(s); } Console.ReadLine(); } } }
در این مثال قصد داریم عناصری که با I (بزرگ) شروع می شوند را در خروجی نشان دهیم. احتمالا انتظار دارید که خروجی دو کلمه ی Iran و India باشد اما این چنین نخواهد بود. به یاد داشته باشید شرط متد تا زمانی معتبر است که نقض نشود. پس به محض نقض شدن شرط اجرای متد TakeWhile
متوقف خواهد شد.
همین مثال را در نظر بگیرید. عضو اول با حرف I شروع می شود پس شرط را ارضا می کند اما عضو دوم با R شروع می شود بنابراین شرط متد نقض شده و فورا اجرای متد متوقف می شود و اهمیت ندارد که عناصر بعدی در شرط صدق می کنند یا نه.
روش Query:
var result = (from x in countries select x).TakeWhile(x => x.StartsWith("I"));
مثال دوم این متد را در نظر بگیرید:
using System; using System.Collections.Generic; using System.Linq; namespace LINQExamples { class Program { static void Main(string[] args) { int[] num = {2,4,6,7,8,10,12}; var result = num.TakeWhile(x => x%2 ==0); foreach (int s in result) { Console.WriteLine(s); } Console.ReadLine(); } } }
یک آرایه از نوع int
در اختیار داریم که همه ی اعضای آن به جز یکی زوج هستند. شرط قرار داده شده در متد TakeWhile
برای گرفتن اعداد زوج موجود در آرایه است.
همان طور که در خروجی مشاهده می کنید فقط سه عضو اول که در شرط صدق می کنند به عنوان خروجی در نظر گرفته شده اند و وجود عنصر چهارم (7) باعث می شود شرط نقض شده و اجرای متد متوقف شود پس عناصر زوجی که بعد از آن قرار دارند در خروجی نشان داده نمی شوند.
روش Query:
var result = (from x in num select x).TakeWhile(x=>x%2==0);
این متد کاملا برعکس متد Take
عمل می کند و تعداد n عنصر اول یک مجموعه را نادیده گرفته و بقیه را به عنوان خروجی در نظر می گیرد.
به الگوی استفاده از آن توجه کنید:
IEnumerable<string> result = list.Skip(n);
مثال اول متد Take
را برای این متد بازنویسی می کنیم:
using System; using System.Collections.Generic; using System.Linq; namespace LINQExamples { class Program { static void Main(string[] args) { string[] countries = { "India", "Iran", "Russia", "China", "USA", "Argentina" }; var result = countries.Skip(3); foreach (string s in result) { Console.WriteLine(s); } Console.ReadLine(); } } }
متد Skip
فقط یک int
به عنوان پارامتر ورودی می گیرد که تعیین می کند که چند عضو از مجموعه نادیده گرفته شوند. در این مثال قصد داریم 3 عنصر اول یک آرایه را نادیده بگیریم و بقیه را در خروجی چاپ کنیم برای این منظور عدد 3 را در متد Skip
قرار داده ایم.
همان طور که مشاهده می کنید از عنصر سوم به بعد در خروجی چاپ شده اند.
روش Query:
var result = (from x in countries select x).Skip(3);
این متد نیز کاملا برعکس متد TakeWhile
عمل می کند. در این متد تا زمانی که شرط برقرار است عناصر مربوطه نادیده گرفته می شوند و باقی عناصر به عنوان خروجی تعیین می گردند.
به الگوی استفاده از آن توجه کنید:
IEnumerable<string> result = list.SkipWhile(Condition");
برای این که تفاوت دو متد را بهتر درک کنید مثال دوم مربوط به متد TakeWhile
را برای این متد بازنویسی می کنیم:
using System; using System.Collections.Generic; using System.Linq; namespace LINQExamples { class Program { static void Main(string[] args) { int[] num = { 2, 4, 6, 7, 8, 10, 12 }; var result = num.SkipWhile(x => x % 2 == 0); foreach (int s in result) { Console.WriteLine(s); } Console.ReadLine(); } } }
در این مثال فقط جای TakeWhile
را با SkipWhile
عوض کرده ایم. کد را اجرا و خروجی را مشاهده کنید. شرط نوشته شده در متد باعث می شود عناصری که در شرط صدق می کنند نادیده گرفته شوند. وجود عضو فرد باعث نقض شدن شرط شده و اجرای متد را متوقف می کند.
بنابراین خروجی به شکل زیر خواهد بود:
حالا ممکن است این سوال پیش بیاید که اصلا این متدها کجا کاربرد دارند و فایده آنها چیست؟
پس به مثال جالب زیر توجه کنید:
using System; using System.Collections.Generic; using System.Linq; namespace LINQExamples { class Program { static void Main(string[] args) { IEnumerable<Student> StuList = Student.GetList(); while (true) { Console.WriteLine("Please enter Page Number 1~5"); int PageNum = 0; if (int.TryParse(Console.ReadLine(), out PageNum)) { if (PageNum >= 1 && PageNum <= 5) { int PageSize = 3; IEnumerable<Student> result = StuList.Skip((PageNum - 1) * PageSize).Take(PageSize); foreach (var item in result) { Console.WriteLine(item.ID + "\t" + item.Name + "\t" + item.Age); } } else { Console.WriteLine("Wrong Number"); } } else { Console.WriteLine("Wrong Number"); } } } } public class Student { public int ID { get; set; } public string Name { get; set; } public int Age { get; set; } public static List<Student> GetList() { return new List<Student>() { new Student(){ ID=1000,Name="Connor",Age=20}, new Student(){ ID=1001,Name="Jacob",Age=22}, new Student(){ ID=1002,Name="Harry",Age=25}, new Student(){ ID=1003,Name="Liam",Age=20}, new Student(){ ID=1004,Name="Olivia",Age=19}, new Student(){ ID=1005,Name="Emily",Age=26}, new Student(){ ID=1006,Name="James",Age=19}, new Student(){ ID=1007,Name="Emma",Age=20}, new Student(){ ID=1008,Name="Mia",Age=25}, new Student(){ ID=1009,Name="Mason",Age=24}, new Student(){ ID=1010,Name="Lily",Age=24}, new Student(){ ID=1011,Name="Thomas",Age=22}, new Student(){ ID=1012,Name="David",Age=21}, new Student(){ ID=1013,Name="Linda",Age=26}, }; } } }
در این مثال با استفاده از دو متد Take
و Skip
عمل صفحه بندی اطلاعات را پیاده سازی کرده ایم. به منظور تکرار کلیه ی عملیات، تمامی کد ها در داخل while
با شرطی همواره صحیح قرار گرفته اند.
ابتدا اطلاعات را توسط متد GetList
که در کلاس Student
تعریف کرده ایم در داخل متغیر StuList
قرار داده ایم. در مرحله ی بعد در if
اول، عدد بودن کاراکتر وارد شده توسط کاربر را کنترل شده و در if
دوم، شرط وارد شدن اعداد 1 تا 5 را اعمال کرده ایم. اگر عدد وارد شده توسط کاربر بین 1 تا 5 باشد مقدار آن در PageNum
ذخیره می شود.
در این مثال می خواهیم در هر صفحه 3 رکورد نمایش داده شود برای این منظور PageSize=3
قرار داده ایم.
اجازه دهید خط کد (StuList.Skip((PageNum - 1) * PageSize).Take(PageSize
را با استفاده از عدد گذاری در آن توضیح دهیم. فرض کنید کاربر، شماره ی صفحه را 2 وارد کرده بنابراین PageNum - 1=1
ضرب در PageSize=3
می شود که حاصل آن عدد 3 خواهد شد.
پس 3 عضو اول مجموعه ی StuList
توسط متد Skip
نادیده گرفته می شوند و باقی در خروجی نمایش داده خواهند شد اما هدف ما نمایش سه رکورد در هر صفحه است، پس باید از عناصر باقی مانده سه عضو اول آن را در اختیار بگیریم که این کار توسط متد Take
انجام خواهد شد و پارامتر ورودی آن نیز PageSize=3
می باشد.
در ادامه هم مانند قبل توسط یک حلقه اطلاعات را در خروجی چاپ می کنیم:
این بخش از آموزش هم به پایان رسید.
موفق باشید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.