다음을 통해 공유


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

업데이트: 2007년 11월

그룹화는 LINQ의 가장 강력한 기능 중 하나입니다. 다음 예제에서는 다양한 방식으로 데이터를 그룹화하는 방법을 보여 줍니다.

  • 단일 속성 사용

  • 문자열 속성의 첫 글자 사용

  • 계산된 숫자 범위 사용

  • 부울 조건자 또는 기타 식 사용

  • 복합 키 사용

또한 마지막 두 개의 쿼리는 학생의 이름과 성만 포함한 새 익명 형식으로 해당 결과를 반환합니다. 자세한 내용은 group 절(C# 참조)을 참조하십시오.

예제

이 항목의 모든 예제에서는 다음 도우미 클래스와 데이터 소스가 사용됩니다.

public class StudentClass
{
    #region data
    protected enum GradeLevel { FirstYear = 1, SecondYear, ThirdYear, FourthYear };
    protected class Student
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int ID { get; set; }
        public GradeLevel Year;
        public List<int> ExamScores;
    }

    protected static List<Student> students = new List<Student>
    {
        new Student {FirstName = "Terry", LastName = "Adams", ID = 120, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 99, 82, 81, 79}},
        new Student {FirstName = "Fadi", LastName = "Fakhouri", ID = 116, Year = GradeLevel.ThirdYear,ExamScores = new List<int>{ 99, 86, 90, 94}},
        new Student {FirstName = "Hanying", LastName = "Feng", ID = 117, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 93, 92, 80, 87}},
        new Student {FirstName = "Cesar", LastName = "Garcia", ID = 114, Year = GradeLevel.FourthYear,ExamScores = new List<int>{ 97, 89, 85, 82}},
        new Student {FirstName = "Debra", LastName = "Garcia", ID = 115, Year = GradeLevel.ThirdYear, ExamScores = new List<int>{ 35, 72, 91, 70}},
        new Student {FirstName = "Hugo", LastName = "Garcia", ID = 118, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 92, 90, 83, 78}},
        new Student {FirstName = "Sven", LastName = "Mortensen", ID = 113, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 88, 94, 65, 91}},
        new Student {FirstName = "Claire", LastName = "O'Donnell", ID = 112, Year = GradeLevel.FourthYear, ExamScores = new List<int>{ 75, 84, 91, 39}},
        new Student {FirstName = "Svetlana", LastName = "Omelchenko", ID = 111, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 97, 92, 81, 60}},
        new Student {FirstName = "Lance", LastName = "Tucker", ID = 119, Year = GradeLevel.ThirdYear, ExamScores = new List<int>{ 68, 79, 88, 92}},
        new Student {FirstName = "Michael", LastName = "Tucker", ID = 122, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 94, 92, 91, 91}},
        new Student {FirstName = "Eugene", LastName = "Zabokritski", ID = 121, Year = GradeLevel.FourthYear, ExamScores = new List<int>{ 96, 85, 91, 60}}
    };
    #endregion

    //Helper method
    protected static int GetPercentile(Student s)
    {
        double avg = s.ExamScores.Average();
        return avg > 0 ? (int)avg / 10 : 0;
    }



    public void QueryHighScores(int exam, int score)
    {
        var highScores = from student in students
                         where student.ExamScores[exam] > score
                         select new {Name = student.FirstName, Score = student.ExamScores[exam]};

        foreach (var item in highScores)
        {
            Console.WriteLine("{0,-15}{1}", item.Name, item.Score);
        }
    }
}

public class Program
{
    public static void Main()
    {
        StudentClass sc = new StudentClass();
        sc.QueryHighScores(1, 90);

        // Keep the console window open in debug mode
        Console.WriteLine("Press any key to exit");
        Console.ReadKey();
    }
}

다음 예제에서는 요소의 단일 속성을 그룹 키로 사용하여 소스 요소를 그룹화하는 방법을 보여 줍니다. 이 경우 키는 string입니다. 또한 부분 문자열을 키에 사용할 수도 있습니다. 그룹화 작업은 형식에 대해 기본 같음 비교자를 사용합니다.

private static void GroupBySingleProperty()
{
    Console.WriteLine("Group by a single property in an object");

    // queryLastNames is an IEnumerable<IGrouping<string, DataClass.Student>>
    // var is easier to type.
    var queryLastNames =
        from student in students
        group student by student.LastName into newGroup
        orderby newGroup.Key
        select newGroup;

    foreach (var nameGroup in queryLastNames)
    {
        Console.WriteLine("Key: {0}", nameGroup.Key);
        foreach (var student in nameGroup)
        {
            Console.WriteLine("\t{0}, {1}", student.LastName, student.FirstName);
        }
    }
}
/* Output:
  Group by a single property in an object
  Key: Feng
          Feng, Hanying
  Key: Garcia
          Garcia, Hugo
          Garcia, Cesar
          Garcia, Debra
  Key: Mortensen
          Mortensen, Sven
  Key: O'Donnell
          O'Donnell, Claire
  Key: Omelchenko
          Omelchenko, Svetlana
  Key: Tucker
          Tucker, Michael
          Tucker, Lance
 */  

다음 예제에서는 개체의 속성이 아닌 항목을 그룹 키에 사용하여 소스 요소를 그룹화하는 방법을 보여 줍니다.

private static void GroupBySubstring()
{            
    Console.WriteLine("\r\nGroup by something other than a property of the object:");

    var queryFirstLetters =
        from student in students
        group student by student.LastName[0];

    foreach (var studentGroup in queryFirstLetters)
    {
        Console.WriteLine("Key: {0}", studentGroup.Key);
        // Nested foreach is required to access group items
        foreach (var student in studentGroup)
        {
            Console.WriteLine("\t{0}, {1}", student.LastName, student.FirstName);
        }
    }           
}
/* Output:
        Group by first character:
        Key: O
                Omelchenko, Svetlana
                O'Donnell, Claire
        Key: G
                Garcia, Hugo
                Garcia, Cesar
                Garcia, Debra
        Key: M
                Mortensen, Sven
        Key: T
                Tucker, Michael
                Tucker, Lance
        Key: F
                Feng, Hanying
     */

다음 예제에서는 숫자 범위를 그룹 키로 사용하여 소스 요소를 그룹화하는 방법을 보여 줍니다. 쿼리는 이름 및 성과 학생이 속한 백분위수 범위만 포함한 익명 형식으로 결과를 반환합니다. 결과를 표시하기 위해 완전한 Student 개체를 사용할 필요가 없으므로 익명 형식이 사용됩니다. GetPercentile은 학생의 평균 점수에 기초하여 백분위수를 계산하는 도우미 함수입니다.

static int GetPercentile(Student s)
{
   double avg = s.Scores.Average();
   return avg > 0 ? (int)avg / 10 : 0;
}
private static void GroupByRange()
{            
    Console.WriteLine("\r\nGroup by numeric range and project into a new anonymous type:");

    var queryNumericRange =
        from student in students
        let percentile = GetPercentile(student)
        group new { student.FirstName, student.LastName } by percentile into percentGroup
        orderby percentGroup.Key
        select percentGroup;

    // Nested foreach required to iterate over groups and group items.
    foreach (var studentGroup in queryNumericRange)
    {
        Console.WriteLine("Key: {0}", (studentGroup.Key * 10));
        foreach (var item in studentGroup)
        {
            Console.WriteLine("\t{0}, {1}", item.LastName, item.FirstName);
        }
    }            
}
/* Output:
     Group by numeric range and project into a new anonymous type:
     Key: 60
             Garcia, Debra
     Key: 70
             Omelchenko, Svetlana
             O'Donnell, Claire
     Key: 80
             Garcia, Hugo
             Mortensen, Sven
             Garcia, Cesar
             Feng, Hanying
             Tucker, Lance
     Key: 90
             Tucker, Michael
     */

다음 예제에서는 부울 비교 식을 사용하여 소스 요소를 그룹화하는 방법을 보여 줍니다. 위의 예제와 같이 완전한 소스 요소가 필요하지 않으므로 결과는 익명 형식으로 반환됩니다. 익명 형식의 속성은 Key 멤버의 속성이 되고 쿼리 실행 시에 이름으로 액세스할 수 있습니다.

private static void GroupByBoolean()
{            
    Console.WriteLine("\r\nGroup by a boolean into two groups with string keys");
    Console.WriteLine("\"True\" and \"False\" and project into a new anonymous type:");
    var queryGroupByAverages = from student in students
                               group new { student.FirstName, student.LastName }
                                    by student.ExamScores.Average() > 75 into studentGroup
                               select studentGroup;

    foreach (var studentGroup in queryGroupByAverages)
    {
        Console.WriteLine("Key: {0}", studentGroup.Key);
        foreach (var student in studentGroup)
            Console.WriteLine("\t{0} {1}", student.FirstName, student.LastName);
    }            
}
/* Output:
         Group by a boolean into two groups with string keys
         "True" and "False" and project into a new anonymous type:
         Key: True
                 Svetlana Omelchenko
                 Hugo Garcia
                 Sven Mortensen
                 Michael Tucker
                 Cesar Garcia
                 Hanying Feng
                 Lance Tucker
         Key: False
                 Claire O'Donnell
                 Debra Garcia
*/

다음 예제에서는 익명 형식을 사용하여 여러 값이 포함된 키를 캡슐화하는 방법을 보여 줍니다. 이 경우 두 번째 키 값은 학생이 첫 번째 시험에서 85점 이상을 받았는지 여부를 지정하는 부울입니다. 키의 임의 속성으로 그룹을 정렬할 수 있습니다.

private static void GroupByCompositeKey()
{

    var queryHighScoreGroups =
        from student in students
        group student by new { FirstLetter = student.LastName[0], Score = student.ExamScores[0] > 85 } into studentGroup
        orderby studentGroup.Key.FirstLetter
        select studentGroup;

    Console.WriteLine("\r\nGroup and order by a compound key:");
    foreach (var scoreGroup in queryHighScoreGroups)
    {
        string s = scoreGroup.Key.Score == true ? "more than" : "less than";
        Console.WriteLine("Name starts with {0} who scored {1} 85", scoreGroup.Key.FirstLetter, s);
        foreach (var item in scoreGroup)
        {
            Console.WriteLine("\t{0} {1}", item.FirstName, item.LastName);
        }
    }
}
/* Output:
          Group and order by a compound key:
          Name starts with F who scored more than 85
                  Hanying Feng
          Name starts with G who scored more than 85
                  Hugo Garcia
                  Cesar Garcia
          Name starts with G who scored less than 85
                  Debra Garcia
          Name starts with M who scored more than 85
                  Sven Mortensen
          Name starts with O who scored more than 85
                  Svetlana Omelchenko
          Name starts with O who scored less than 85
                  Claire O'Donnell
          Name starts with T who scored more than 85
                  Michael Tucker
          Name starts with T who scored less than 85
                  Lance Tucker
       */

코드 컴파일

이 예제에는 방법: 개체 컬렉션 쿼리(C# 프로그래밍 가이드)에서 샘플 응용 프로그램에 정의된 개체에 대한 참조가 포함되어 있습니다. 이 메서드를 컴파일하고 실행하려면 해당 응용 프로그램의 StudentClass 클래스에 이 메서드를 붙여넣고 이 메서드에 대한 Main 메서드의 호출을 추가합니다.

사용자 고유의 응용 프로그램에 이 메서드를 적용하는 경우 LINQ에서는 .NET Framework 버전 3.5를 필요로 하며 프로젝트에는 System.Core.dll에 대한 참조와 System.Linq에 대한 using 지시문이 포함되어야 합니다. LINQ to SQL, LINQ to XML 및 LINQ to DataSet 형식에는 추가 usings 및 참조가 필요합니다. 자세한 내용은 방법: LINQ 프로젝트 만들기를 참조하십시오.

참고 항목

작업

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

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

개념

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

데이터 그룹화

참조

group 절(C# 참조)

익명 형식(C# 프로그래밍 가이드)

GroupBy

IGrouping<TKey, TElement>