共用方式為


group 子句 (C# 參考)

group 子句會傳回一系列的 IGrouping<TKey,TElement> 物件,而這些物件包含符合群組之索引鍵值的零或多個項目。 例如,您可以根據每個字串中的第一個字母來分組一序列的字串。 在此情況下,第一個字母是索引鍵、具有類型 char,並儲存在每個 IGrouping<TKey,TElement> 物件的 Key 屬性中。 編譯器會推斷索引鍵類型。

您可以使用 group 子句結束查詢運算式,如下列範例所示︰

// Query variable is an IEnumerable<IGrouping<char, Student>>
var studentQuery1 =
    from student in students
    group student by student.Last[0];

如果您想要對每個群組執行其他查詢作業,則可以使用 into 內容關鍵字來指定暫時識別碼。 當您使用 into 時,必須繼續進行查詢,最後以 select 陳述式或另一個 group 子句結束,如下列摘錄所示︰

// Group students by the first letter of their last name
// Query variable is an IEnumerable<IGrouping<char, Student>>
var studentQuery2 =
    from student in students
    group student by student.Last[0] into g
    orderby g.Key
    select g;

本文的<範例>一節中提供使用或未使用 intogroup 的更完整範例。

列舉群組查詢結果

因為 group 查詢所產生的 IGrouping<TKey,TElement> 物件基本上是清單的清單,所以您必須使用巢狀 foreach 迴圈來存取每個群組中的項目。 外部迴圈會逐一查看群組索引鍵,內部迴圈則會逐一查看群組本身中的每個項目。 群組可能具有索引鍵,但沒有項目。 下列 foreach 迴圈會執行先前程式碼範例中的查詢︰

// Iterate group items with a nested foreach. This IGrouping encapsulates
// a sequence of Student objects, and a Key of type char.
// For convenience, var can also be used in the foreach statement.
foreach (IGrouping<char, Student> studentGroup in studentQuery2)
{
     Console.WriteLine(studentGroup.Key);
     // Explicit type for student could also be used here.
     foreach (var student in studentGroup)
     {
         Console.WriteLine("   {0}, {1}", student.Last, student.First);
     }
 }

索引鍵類型

群組索引鍵可以是任何類型,例如字串、內建數值類型,或使用者定義的具名類型或匿名型別。

依字串群組

先前的程式碼範例已使用 char。 可以改為輕鬆地指定字串索引鍵,例如完整姓氏︰

// Same as previous example except we use the entire last name as a key.
// Query variable is an IEnumerable<IGrouping<string, Student>>
var studentQuery3 =
    from student in students
    group student by student.Last;

依 bool 群組

下列範例示範如何使用索引鍵的 bool 值,以將結果分成兩個群組。 請注意,值是由 group 子句中的子運算式所產生。

class GroupSample1
{
    // The element type of the data source.
    public class Student
    {
        public required string First { get; init; }
        public required string Last { get; init; }
        public required int ID { get; init; }
        public required List<int> Scores;
    }

    public static List<Student> GetStudents()
    {
        // Use a collection initializer to create the data source. Note that each element
        //  in the list contains an inner sequence of scores.
        List<Student> students =
        [
           new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= [97, 72, 81, 60]},
           new Student {First="Claire", Last="O'Donnell", ID=112, Scores= [75, 84, 91, 39]},
           new Student {First="Sven", Last="Mortensen", ID=113, Scores= [99, 89, 91, 95]},
           new Student {First="Cesar", Last="Garcia", ID=114, Scores= [72, 81, 65, 84]},
           new Student {First="Debra", Last="Garcia", ID=115, Scores= [97, 89, 85, 82]}
        ];

        return students;
    }

    static void Main()
    {
        // Obtain the data source.
        List<Student> students = GetStudents();

        // Group by true or false.
        // Query variable is an IEnumerable<IGrouping<bool, Student>>
        var booleanGroupQuery =
            from student in students
            group student by student.Scores.Average() >= 80; //pass or fail!

        // Execute the query and access items in each group
        foreach (var studentGroup in booleanGroupQuery)
        {
            Console.WriteLine(studentGroup.Key == true ? "High averages" : "Low averages");
            foreach (var student in studentGroup)
            {
                Console.WriteLine("   {0}, {1}:{2}", student.Last, student.First, student.Scores.Average());
            }
        }
    }
}
/* Output:
  Low averages
   Omelchenko, Svetlana:77.5
   O'Donnell, Claire:72.25
   Garcia, Cesar:75.5
  High averages
   Mortensen, Sven:93.5
   Garcia, Debra:88.25
*/

依數字範圍群組

下一個範例使用運算式來建立代表百分位數範圍的數字群組索引鍵。 請注意會使用 let 作為儲存方法呼叫結果的方便位置,因此不需要在 group 子句中呼叫方法兩次。 如需如何在查詢運算式中安全地使用方法的詳細資訊,請參閱處理查詢運算式中的例外狀況

class GroupSample2
{
    // The element type of the data source.
    public class Student
    {
        public required string First { get; init; }
        public required string Last { get; init; }
        public required int ID { get; init; }
        public required List<int> Scores;
    }

    public static List<Student> GetStudents()
    {
        // Use a collection initializer to create the data source. Note that each element
        //  in the list contains an inner sequence of scores.
        List<Student> students =
        [
           new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= [97, 72, 81, 60]},
           new Student {First="Claire", Last="O'Donnell", ID=112, Scores= [75, 84, 91, 39]},
           new Student {First="Sven", Last="Mortensen", ID=113, Scores= [99, 89, 91, 95]},
           new Student {First="Cesar", Last="Garcia", ID=114, Scores= [72, 81, 65, 84]},
           new Student {First="Debra", Last="Garcia", ID=115, Scores= [97, 89, 85, 82]}
        ];

        return students;
    }

    // This method groups students into percentile ranges based on their
    // grade average. The Average method returns a double, so to produce a whole
    // number it is necessary to cast to int before dividing by 10.
    static void Main()
    {
        // Obtain the data source.
        List<Student> students = GetStudents();

        // Write the query.
        var studentQuery =
            from student in students
            let avg = (int)student.Scores.Average()
            group student by (avg / 10) into g
            orderby g.Key
            select g;

        // Execute the query.
        foreach (var studentGroup in studentQuery)
        {
            int temp = studentGroup.Key * 10;
            Console.WriteLine("Students with an average between {0} and {1}", temp, temp + 10);
            foreach (var student in studentGroup)
            {
                Console.WriteLine("   {0}, {1}:{2}", student.Last, student.First, student.Scores.Average());
            }
        }
    }
}
/* Output:
     Students with an average between 70 and 80
       Omelchenko, Svetlana:77.5
       O'Donnell, Claire:72.25
       Garcia, Cesar:75.5
     Students with an average between 80 and 90
       Garcia, Debra:88.25
     Students with an average between 90 and 100
       Mortensen, Sven:93.5
 */

依複合索引鍵群組

當您想要根據多個索引鍵來群組項目時,請使用複合索引鍵。 您可以使用匿名型別或具名類型來保存索引鍵項目,以建立複合索引鍵。 在下列範例中,假設已宣告 Person 類別具有名為 surnamecity 的成員。 group 子句會為每一組具有相同姓氏和相同城市的人員,建立個別群組。

group person by new {name = person.surname, city = person.city};

如果您必須將查詢變數傳遞給另一種方法,請使用具名類型。 使用自動實作的索引鍵屬性建立特殊類別,然後覆寫 EqualsGetHashCode 方法。 您也可以使用結構,在此情況下,您絕對不需要覆寫這些方法。 如需詳細資訊,請參閱 如何使用自動實作的屬性 來實作輕量型類別,以及如何 查詢目錄樹狀目錄中重複的檔案。 第二篇文章的程式碼範例示範如何使用含有具名類型的複合索引鍵。

範例 1

下列範例示範未將任何其他查詢邏輯套用到群組時,將來源資料排序成群組的標準模式。 這稱為無接續群組。 字串陣列中的項目是根據其第一個字母進行分組。 查詢的結果是包含 char 類型之公用 Key 屬性的 IGrouping<TKey,TElement> 類型,以及包含群組中各個項目的 IEnumerable<T> 集合。

group 子句的結果是一連串的序列。 因此,若要存取每個所傳回群組內的個別項目,請在重複執行群組索引鍵的迴圈內使用巢狀 foreach 迴圈,如下列範例所示。

class GroupExample1
{
    static void Main()
    {
        // Create a data source.
        string[] words = ["blueberry", "chimpanzee", "abacus", "banana", "apple", "cheese"];

        // Create the query.
        var wordGroups =
            from w in words
            group w by w[0];

        // Execute the query.
        foreach (var wordGroup in wordGroups)
        {
            Console.WriteLine("Words that start with the letter '{0}':", wordGroup.Key);
            foreach (var word in wordGroup)
            {
                Console.WriteLine(word);
            }
        }
    }
}
/* Output:
      Words that start with the letter 'b':
        blueberry
        banana
      Words that start with the letter 'c':
        chimpanzee
        cheese
      Words that start with the letter 'a':
        abacus
        apple
     */

範例 2

這個範例示範如何搭配使用「接續」into,以在建立其他邏輯之後,對群組執行這些邏輯。 如需詳細資訊,請參閱 into。 下列範例會查詢每個群組,只選取其索引鍵值是母音的群組。

class GroupClauseExample2
{
    static void Main()
    {
        // Create the data source.
        string[] words2 = ["blueberry", "chimpanzee", "abacus", "banana", "apple", "cheese", "elephant", "umbrella", "anteater"];

        // Create the query.
        var wordGroups2 =
            from w in words2
            group w by w[0] into grps
            where (grps.Key == 'a' || grps.Key == 'e' || grps.Key == 'i'
                   || grps.Key == 'o' || grps.Key == 'u')
            select grps;

        // Execute the query.
        foreach (var wordGroup in wordGroups2)
        {
            Console.WriteLine("Groups that start with a vowel: {0}", wordGroup.Key);
            foreach (var word in wordGroup)
            {
                Console.WriteLine("   {0}", word);
            }
        }
    }
}
/* Output:
    Groups that start with a vowel: a
        abacus
        apple
        anteater
    Groups that start with a vowel: e
        elephant
    Groups that start with a vowel: u
        umbrella
*/

備註

在編譯時期,group 子句會轉譯成 GroupBy 方法的呼叫。

group 子句查詢的語法不支援自訂相等比較子。 如果您想要在查詢中使用 IEqualityComparer,請明確地使用 GroupBy 方法。

另請參閱