group 절(C# 참조)

업데이트: 2007년 11월

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;

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;

부울 값으로 그룹화

다음 예제에서는 키에 부울 값을 사용하여 결과를 두 개의 그룹으로 나누는 방법을 보여 줍니다. group 절의 하위 식에서 값이 생성됩니다.

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

숫자 범위로 그룹화

다음 예제에서는 식을 사용하여 백분위수 범위를 나타내는 숫자 그룹 키를 만듭니다. group 절에서 메서드를 두 번 호출할 필요가 없도록 메서드 호출 결과를 저장할 편리한 위치로 let을 사용합니다. 또한 "0으로 나누기" 예외를 방지하기 위해 이 코드는 group 절에서 학생의 평균이 0이 아닌지 확인합니다. 쿼리 식에서 메서드를 안전하게 사용하는 방법에 대한 자세한 내용은 방법: 쿼리 식의 예외 처리(C# 프로그래밍 가이드)를 참조하십시오.

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 == 0 ? 0 : 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
 */

복합 키로 그룹화

둘 이상의 키에 따라 요소를 그룹화하려는 경우 복합 키를 사용합니다. 익명 형식이나 명명된 형식으로 복합 키를 만들어 키 요소를 포함합니다. 다음 예제에서는 Person 클래스가 surname 및 city라는 멤버로 선언되었다고 가정합니다. group 클래스는 성과 구/군/시가 같은 각 개인 집합에 대해 별도의 그룹이 만들어지게 합니다.

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

쿼리 변수를 다른 메서드로 전달해야 하는 경우 명명된 형식을 사용합니다. 키에 대해 자동 구현된 속성을 사용하여 특수 클래스를 만든 다음 EqualsGetHashCode 메서드를 재정의합니다. 이러한 메서드를 반드시 재정의할 필요가 없는 구조체를 사용할 수도 있습니다. 자세한 내용은 방법: 자동으로 구현된 속성을 사용하여 간단한 클래스 구현(C# 프로그래밍 가이드)방법: 디렉터리 트리의 중복 파일 쿼리(LINQ)을 참조하십시오. 두 번째 항목에는 복합 키에 명명된 형식을 사용하는 방법을 보여 주는 코드 예제가 있습니다.

예제

다음 예제에서는 그룹에 적용되는 추가 쿼리 논리가 없을 때 소스 데이터를 그룹으로 정렬하기 위한 표준 패턴을 보여 줍니다. 이를 비연속 그룹화라고 합니다. 문자열 배열의 요소는 첫 글자에 따라 그룹화됩니다. 쿼리 결과는 그룹의 각 항목을 포함하는 IEnumerable<T> 컬렉션 및 char 형식의 public Key 속성을 포함하는 IGrouping<TKey, TElement> 형식입니다.

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

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

이 예제에서는 연속에 into를 사용하여 그룹을 만든 후 그룹에서 추가 논리를 수행하는 방법을 보여 줍니다. 자세한 내용은 into(C# 참조)를 참조하십시오. 다음 예제에서는 각 그룹을 쿼리하여 키 값이 모음인 그룹만 선택합니다.

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

설명

컴파일 타임에 group 절은 GroupBy 메서드에 대한 호출로 변환됩니다.

참고 항목

작업

방법: 그룹 그룹화(C# 프로그래밍 가이드)

방법: 다양한 방법으로 결과 그룹화(C# 프로그래밍 가이드)

방법: 그룹화 작업에서 하위 쿼리 수행(C# 프로그래밍 가이드)

개념

LINQ 쿼리 식(C# 프로그래밍 가이드)

참조

IGrouping<TKey, TElement>

GroupBy

ThenBy

ThenByDescending

기타 리소스

쿼리 키워드(C# 참조)