عبارت group by

عبارت group by با استفاده از مقدار یک کلید، آیتم‌ها را گروه بندی می‌کند. این عبارت می‌تواند بین عبارت from و select یا در آخر عبارت پرس و جو قرار گیرد. ساختار ابتدایی یک عبارت group by به شکل زیر است :

group item by key into groupVar

عبارت group-by با کلمه کلیدی group و نام آیتمی که قصد گروه بندی آن را داریم شروع می‌شود. سپس بعد از آن، کلمه کلیدی by را که کلید گروه بندی را مشخص می‌کند می‌نویسیم. به عنوان مثال کلید گروه بندی می‌تواند نام شهر باشد تا تمامی آیتم‌هایی که دارای شهر یکسانی هستند در یک گروه قرار گیرند. و در آخر کلمه کلیدی into با نام گروهی جدیدی که آیتم در آن قرار می‌گیرد (groupVar) نوشته می‌شود. groupVar دارای نوع System.Linq.IGrouping<TKey, TElement> می‌باشد که مشخص می‌کند که کلید گروه از نوع TKey و آیتم‌های گروه از نوع TElement است. به عنوان مثال، فرض کنید شما می‌خواهید تعدادی بازیکن را در تیم‌های مختلف قرار دهید. در زیر کلاسی با نام Player (خطوط 10-6) با دو خاصیت Name و Team تعریف شده است.

   1: using System;
   2: using System.Collections.Generic;
   3: using System.IO;
   4: using System.Linq;
   5: 
   6: public class Player
   7: {
   8:     public string Name { get; set; }
   9:     public string Team { get; set; }
  10: }
  11: 
  12: public class Program
  13: {
  14:     public static void Main()
  15:     {
  16:        List<Player> players = new List<Player>
  17:        {
  18:            new Player { Name = "Johnny", Team= "Red Team" },
  19:            new Player { Name = "Ross", Team = "Blue Team" },
  20:            new Player { Name = "Eric", Team = "Black Team" },
  21:            new Player { Name = "Josh", Team = "White Team" },
  22:            new Player { Name = "Mandy", Team = "Blue Team" },
  23:            new Player { Name = "Flora", Team = "White Team" },
  24:            new Player { Name = "Garry", Team = "Red Team" },
  25:            new Player { Name = "Joseph", Team = "Blue Team"},
  26:            new Player { Name = "Murray", Team = "Black Team"},
  27:            new Player { Name = "Henry", Team = "Black Team"},
  28:            new Player { Name = "Watson", Team = "Red Team"},
  29:            new Player { Name = "Linda", Team = "White Team"}
  30:        };
  31: 
  32:        var groups = from p in players
  33:                     group p by p.Team into g                      
  34:                     select new { GroupName = g.Key, Members = g };
  35: 
  36:        foreach (var g in groups)
  37:        {
  38:            Console.WriteLine("Members of {0}", g.GroupName);
  39: 
  40:            foreach (var member in g.Members)
  41:            {
  42:                Console.WriteLine("---{0}", member.Name);
  43:            }
  44:        }
  45:     }
  46: }
Members of Red Team
---Johnny
---Garry
---Watson
Members of Blue Team
---Ross
---Mandy
---Joseph
Members of Black Team
---Eric
---Murray
---Henry
Members of White Team
---Josh
---Flora
---Linda

در خطوط 30-16 لیستی از بازیکن‌ها را ایجاد می‌کنیم. قصد داریم که بازیکن‌ها را بر اساس نام تیم گروه بندی کنیم، به صورتی که بازیکن‌های تیم‌های مشابه در یک گروه قرار گیرند. عبارت پرس و جو در خطوط 34-32 از بک عبارت group by برای گروه بندی استفاده کرده است. بازیکن‌های با مقدار خاصیت Team مشابه در یک گروه که رابط IGrouping<string, Player> را پیاده سازی می‌کند قرار می‌گیرند.
از آنجاییکه ما از Team که از نوع رشته است به عنوان کلید استفاده کرده‌ایم خاصیت Key هم از نوع رشته خواهد بود. سپس در عبارت select (خط 34) یک نوع بی نام با دو خاصیت GroupName و Members را ساخته و به ترتیب مقدار کلید و اعضای گروه را به آنها نسبت می‌دهیم. خطوط 44-36 با استفاده از حلقه foreach پرس و جو را اجرا می‌کند
برای چاپ مقادیر نیاز به دو حلقه foreach داریم. حلقه اول برای چرخش در بین گروه‌های مختلف و چاپ نام آنها و حلقه دوم برای چاپ اعضای گروه‌ها. برای چاپ نام از مقدار خاصیت GroupName و برای چاپ اعضای گروه از خاصیت Members استفاده می‌کنیم.

خاتمه یک عبارت پرس و جو با استفاده از عبارت group by

عبارت group by می‌تواند در انتهای عبارت پرس و جو نیز قرار گیرد. به خاطر داشته باشید که یک عبارت پرس و جو می‌تواند با یک عبارت select یا group by خاتمه پیدا کند. در زیر مثالی را مشاهده می‌کنید که در آن عبارت پرس وجو با یک عبارت group by خاتمه پیدا می‌کند.

var groups = from p in players
             group p by p.Team;

foreach (var g in groups)
{
    Console.WriteLine("Members of {0}", g.Key);

    foreach (var member in g)
    {
        Console.WriteLine("---{0}", member.Name);
    }
}

عبارت پرس وجو بالا نتیجه‌ای مشابه مثال قبلی تولید می‌کند. نوع نتیجه کوئری IEnumerable<IGrouping<string, Player>> است، به این معنی که شامل مجموعه‌ای از گروه‌ها با کلیدی از نوع رشته و اشیاء Player می‌باشد. در داخل اولین حلقه foreach، ابتدا با استفاده از مقدار Key هر گروه نام گروه را در خروجی می‌نویسیم. حلقه داخلی برای چاپ اعضای گروه از نام متغیر گروه به عنوان منبع داده استفاده می‌کند. نکته قابل توجه این است که هر متغیر موقت قبل از عبارت group by بعد از آن قابل دسترسی نیست.

متد GroupBy()

هر عبارت group-by به یک فراخوانی متد GroupBy() تبدیل می‌شود که یک متد توسعه یافته از رابط IEnumerable<T> است. مثال زیر به شما نشان می‌دهد که چگونه عبارت پرس وجو بالا را با استفاده از شکل متدی بنویسید :

var groups = players.GroupBy(p => p.Team);

متد GroupBy() یک عبارت لامبدا که دارای یک پارامتر و یک خروجی است را به عنوان پارامتر قبول می‌کند. پارامتر و خروجی عبارت لامبدا به ترتیب بیانگر یک عضو گروه و کلید گروه بندی می‌باشند. عبارت پرس و جو مثال قبلی را می‌توانید به شکل زیر نیز بنویسید :

var groups = players.GroupBy(p => p.Team)
                    .Select(g => new { GroupName = g.Key, Members = g });