방법: 다양한 방법으로 결과 그룹화(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# 프로그래밍 가이드)