group 절(C# 참조)group clause (C# Reference)

group 절은 그룹의 키 값과 일치하는 0개 이상의 항목이 포함된 IGrouping<TKey,TElement> 개체 시퀀스를 반환합니다.The group clause returns a sequence of IGrouping<TKey,TElement> objects that contain zero or more items that match the key value for the group. 예를 들어 각 문자열의 첫 번째 문자에 따라 문자열 시퀀스를 그룹화할 수 있습니다.For example, you can group a sequence of strings according to the first letter in each string. 이 경우 첫 번째 문자는 키로, char 형식이며 각 IGrouping<TKey,TElement> 개체의 Key 속성에 저장됩니다.In this case, the first letter is the key and has a type char, and is stored in the Key property of each IGrouping<TKey,TElement> object. 컴파일러는 키의 형식을 유추합니다.The compiler infers the type of the key.

다음 예제와 같이 쿼리 식을 group 절로 끝낼 수 있습니다.You can end a query expression with a group clause, as shown in the following example:

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

각 그룹에서 추가 쿼리 작업을 수행하려는 경우 into 상황별 키워드를 사용하여 임시 식별자를 지정할 수 있습니다.If you want to perform additional query operations on each group, you can specify a temporary identifier by using the into contextual keyword. into를 사용하는 경우 쿼리를 계속 진행하고, 다음 예제와 같이 궁극적으로 select 문이나 다른 group 절로 끝내야 합니다.When you use into, you must continue with the query, and eventually end it with either a select statement or another group clause, as shown in the following excerpt:

// 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을 사용하는 보다 자세한 예제는 이 문서의 예제 섹션에 제공됩니다.More complete examples of the use of group with and without into are provided in the Example section of this article.

그룹 쿼리의 결과 열거Enumerating the results of a group query

group 쿼리에 의해 생성된 IGrouping<TKey,TElement> 개체는 기본적으로 목록의 목록이기 때문에 중첩된 foreach 루프를 사용하여 각 그룹에 있는 항목에 액세스해야 합니다.Because the IGrouping<TKey,TElement> objects produced by a group query are essentially a list of lists, you must use a nested foreach loop to access the items in each group. 외부 루프는 그룹 키를 반복하고, 내부 루프는 그룹 자체에 있는 각 항목을 반복합니다.The outer loop iterates over the group keys, and the inner loop iterates over each item in the group itself. 그룹에 키가 있지만 요소는 없을 수도 있습니다.A group may have a key but no elements. 다음은 앞의 코드 예제에서 쿼리를 실행하는 foreach 루프입니다.The following is the foreach loop that executes the query in the previous code examples:

// 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);
     }
 }

키 형식Key types

그룹 키는 문자열, 기본 제공 숫자 형식, 사용자 정의 명명된 형식, 무명 형식 등 모든 형식일 수 있습니다.Group keys can be any type, such as a string, a built-in numeric type, or a user-defined named type or anonymous type.

문자열로 그룹화Grouping by string

앞의 코드 예제에서는 char를 사용했습니다.The previous code examples used a char. 대신, 문자열 키를 전체 성 등으로 쉽게 지정했을 수 있습니다.A string key could easily have been specified instead, for example the complete last name:

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

부울로 그룹화Grouping by bool

다음 예제에서는 키에 부울 값을 사용하여 결과를 두 그룹으로 나누는 방법을 보여 줍니다.The following example shows the use of a bool value for a key to divide the results into two groups. 값은 group 절의 하위 식에서 생성됩니다.Note that the value is produced by a sub-expression in the group clause.

class GroupSample1
{
    // The element type of the data source.
    public class Student
    {
        public string First { get; set; }
        public string Last { get; set; }
        public int ID { get; set; }
        public 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 List<Student>
        {
           new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List<int> {97, 72, 81, 60}},
           new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new List<int> {75, 84, 91, 39}},
           new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List<int> {99, 89, 91, 95}},
           new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List<int> {72, 81, 65, 84}},
           new Student {First="Debra", Last="Garcia", ID=115, Scores= new List<int> {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());
            }
        }

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* 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
*/

숫자 범위로 그룹화Grouping by numeric range

다음 예제에서는 식을 사용하여 백분위수 범위를 나타내는 숫자 그룹 키를 만듭니다.The next example uses an expression to create numeric group keys that represent a percentile range. 메서드 호출 결과를 저장할 편리한 위치로 let을 사용하여 group 절에서 메서드를 두 번 호출할 필요가 없도록 합니다.Note the use of let as a convenient location to store a method call result, so that you don't have to call the method two times in the group clause. 쿼리 식에 메서드를 안전하게 사용하는 방법에 대한 자세한 내용은 쿼리 식의 예외 처리를 참조하세요.For more information about how to safely use methods in query expressions, see Handle exceptions in query expressions.

class GroupSample2
{
    // The element type of the data source.
    public class Student
    {
        public string First { get; set; }
        public string Last { get; set; }
        public int ID { get; set; }
        public 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 List<Student>
        {
           new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List<int> {97, 72, 81, 60}},
           new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new List<int> {75, 84, 91, 39}},
           new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List<int> {99, 89, 91, 95}},
           new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List<int> {72, 81, 65, 84}},
           new Student {First="Debra", Last="Garcia", ID=115, Scores= new List<int> {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());
            }
        }

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* 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
 */

복합 키로 그룹화Grouping by composite keys

둘 이상의 키에 따라 요소를 그룹화하려는 경우 복합 키를 사용합니다.Use a composite key when you want to group elements according to more than one key. 무명 형식이나 명명된 형식을 통해 키 요소를 저장하여 복합 키를 만듭니다.You create a composite key by using an anonymous type or a named type to hold the key element. 다음 예제에서는 Person 클래스가 surnamecity라는 멤버로 선언되었다고 가정합니다.In the following example, assume that a class Person has been declared with members named surname and city. group 절은 성과 도시가 동일한 각 개인 집합에 대해 별도 그룹이 생성되도록 합니다.The group clause causes a separate group to be created for each set of persons with the same last name and the same city.

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

쿼리 변수를 다른 메서드에 전달해야 하는 경우 명명된 형식을 사용합니다.Use a named type if you must pass the query variable to another method. 키에 대해 자동 구현 속성을 사용하여 특수 클래스를 만든 다음 EqualsGetHashCode 메서드를 재정의합니다.Create a special class using auto-implemented properties for the keys, and then override the Equals and GetHashCode methods. 구조체를 사용할 수도 있으며, 이 경우 이러한 메서드를 엄격하게 재정의하지 않아도 됩니다.You can also use a struct, in which case you do not strictly have to override those methods. 자세한 내용은 자동으로 구현된 속성을 사용하여 간단한 클래스를 구현하는 방법디렉터리 트리의 중복 파일을 쿼리하는 방법을 참조하세요.For more information see How to implement a lightweight class with auto-implemented properties and How to query for duplicate files in a directory tree. 두 번째 문서에는 명명된 형식과 함께 복합 키를 사용하는 방법을 보여 주는 코드 예제가 있습니다.The latter article has a code example that demonstrates how to use a composite key with a named type.

예제Example

다음 예제에서는 추가 쿼리 논리가 그룹에 적용되지 않는 경우 소스 데이터를 그룹으로 정렬하기 위한 표준 패턴을 보여 줍니다.The following example shows the standard pattern for ordering source data into groups when no additional query logic is applied to the groups. 이를 비연속 그룹화라고 합니다.This is called a grouping without a continuation. 문자열 배열에 있는 요소는 첫 문자에 따라 그룹화됩니다.The elements in an array of strings are grouped according to their first letter. 쿼리 결과는 그룹에 각 항목을 포함하는 공용 Key 속성 형식 charIEnumerable<T> 컬렉션을 포함하는 IGrouping<TKey,TElement> 형식입니다.The result of the query is an IGrouping<TKey,TElement> type that contains a public Key property of type char and an IEnumerable<T> collection that contains each item in the grouping.

group 절의 결과는 시퀀스의 시퀀스입니다.The result of a group clause is a sequence of sequences. 따라서 반환된 각 그룹 내의 개별 요소에 액세스하려면 다음 예제와 같이 그룹 키를 반복하는 루프 안에 중첩된 foreach 루프를 사용합니다.Therefore, to access the individual elements within each returned group, use a nested foreach loop inside the loop that iterates the group keys, as shown in the following example.

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);
            }
        }

        // Keep the console window open in debug mode
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* 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
     */

예제Example

이 예제에서는 into와 함께 continuation 을 사용하여 그룹을 만든 후 그룹에서 추가 논리를 수행하는 방법을 보여 줍니다.This example shows how to perform additional logic on the groups after you have created them, by using a continuation with into. 자세한 내용은 into를 참조하세요.For more information, see into. 다음 예제에서는 각 그룹을 쿼리하여 키 값이 모음인 그룹만 선택합니다.The following example queries each group to select only those whose key value is a vowel.

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);
            }
        }

        // Keep the console window open in debug mode
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* 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
*/

설명Remarks

group 절은 컴파일 시간에 GroupBy 메서드 호출로 변환됩니다.At compile time, group clauses are translated into calls to the GroupBy method.

참조See also