次の方法で共有


group 句 (C# リファレンス)

group 句は、グループのキー値に一致する 0 個以上の項目を含む 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;

この記事の「例」のセクションでは、into を含む場合と含まない場合の group の使用方法の完全な例があります。

グループ クエリの結果を列挙する

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;

ブールでグループ化する

次の例では、結果を 2 つのグループに分割するためのキー用のブール値の用途を示します。 値は 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
*/

数値の範囲でグループ化する

次の例では、パーセンタイルの範囲を示す数値のグループ キーを作成する式を使用しています。 group 句でメソッドを 2 度呼び出さなくて済むように、メソッド呼び出しの結果を格納する便利な場所として let を使用できます。 クエリ式でメソッドを安全に使用する方法の詳細については、「クエリ式の例外の処理」を参照してください。

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
 */

複合キーでグループ化する

1 つ以上のキーを使用して要素をグループ化するには、複合キーを使用します。 複合キーは、キー要素を保持する、匿名型または名前付きの型を使用して作成できます。 次の例では、クラス Person が、surnamecity という名前のメンバーで宣言されていると想定しています。 group 句により、同じ名前と同じ市の人物のセットごとに、別のグループが作成されます。

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

クエリ変数を別のメソッドに渡す場合には、名前付きの型を使用します。 キーに対して自動的に実装されるプロパティを使用して特別なクラスを作成し、 Equals メソッドと GetHashCode メソッドをオーバーライドします。 これらのメソッドを厳密にオーバーライドする必要がない構造体を使用することも可能です。 詳細については、「 自動的に実装されるプロパティを持つ軽量クラスを実装する方法 および ディレクトリ ツリー内の重複するファイルを照会する方法を参照してください。 後述の記事には、名前付きの型を複合キーで使用する方法のコード例があります。

例 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 と共に continuation を使用し、グループに追加のロジックを実行する方法を示します。 詳しくは、「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 メソッドを使用します。

関連項目