방법: 쿼리 결과 그룹화(C# 프로그래밍 가이드)
그룹화는 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, used in GroupByRange.
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입니다.또한 부분 문자열을 키에 사용할 수도 있습니다.그룹화 작업은 형식에 대해 기본 같음 비교자를 사용합니다.
다음 메서드를 StudentClass 클래스에 붙여 넣습니다.Main 메서드의 호출 문을 sc.GroupBySingleProperty()로 변경합니다.
public void GroupBySingleProperty()
{
Console.WriteLine("Group by a single property in an object:");
// Variable queryLastNames is an IEnumerable<IGrouping<string,
// DataClass.Student>>.
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: Adams
Adams, Terry
Key: Fakhouri
Fakhouri, Fadi
Key: Feng
Feng, Hanying
Key: Garcia
Garcia, Cesar
Garcia, Debra
Garcia, Hugo
Key: Mortensen
Mortensen, Sven
Key: O'Donnell
O'Donnell, Claire
Key: Omelchenko
Omelchenko, Svetlana
Key: Tucker
Tucker, Lance
Tucker, Michael
Key: Zabokritski
Zabokritski, Eugene
*/
다음 예제에서는 개체의 속성이 아닌 항목을 그룹 키에 사용하여 소스 요소를 그룹화하는 방법을 보여 줍니다.이 예제에서 키는 학생의 성 중에서 첫 번째 문자입니다.
다음 메서드를 StudentClass 클래스에 붙여 넣습니다.Main 메서드의 호출 문을 sc.GroupBySubstring()로 변경합니다.
public 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 something other than a property of the object:
Key: A
Adams, Terry
Key: F
Fakhouri, Fadi
Feng, Hanying
Key: G
Garcia, Cesar
Garcia, Debra
Garcia, Hugo
Key: M
Mortensen, Sven
Key: O
O'Donnell, Claire
Omelchenko, Svetlana
Key: T
Tucker, Lance
Tucker, Michael
Key: Z
Zabokritski, Eugene
*/
다음 예제에서는 숫자 범위를 그룹 키로 사용하여 소스 요소를 그룹화하는 방법을 보여 줍니다.쿼리는 이름 및 성과 학생이 속한 백분위수 범위만 포함한 익명 형식으로 결과를 반환합니다.결과를 표시하기 위해 완전한 Student 개체를 사용할 필요가 없으므로 익명 형식이 사용됩니다.GetPercentile은 학생의 평균 점수에 기초하여 백분위수를 계산하는 도우미 함수입니다.이 메서드는 0에서 10 사이의 정수를 반환합니다.
//Helper method, used in GroupByRange.
protected static int GetPercentile(Student s)
{
double avg = s.ExamScores.Average();
return avg > 0 ? (int)avg / 10 : 0;
}
다음 메서드를 StudentClass 클래스에 붙여 넣습니다.Main 메서드의 호출 문을 sc.GroupByRange()로 변경합니다.
public 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
O'Donnell, Claire
Key: 80
Adams, Terry
Feng, Hanying
Garcia, Cesar
Garcia, Hugo
Mortensen, Sven
Omelchenko, Svetlana
Tucker, Lance
Zabokritski, Eugene
Key: 90
Fakhouri, Fadi
Tucker, Michael
*/
다음 예제에서는 부울 비교 식을 사용하여 소스 요소를 그룹화하는 방법을 보여 줍니다.이 예제에서 부울 식은 학생의 평균 시험 점수가 75점을 넘는지 확인합니다.위의 예제와 같이 완전한 소스 요소가 필요하지 않으므로 결과는 익명 형식으로 반환됩니다.익명 형식의 속성은 Key 멤버의 속성이 되고 쿼리 실행 시에 이름으로 액세스할 수 있습니다.
다음 메서드를 StudentClass 클래스에 붙여 넣습니다.Main 메서드의 호출 문을 sc.GroupByBoolean()로 변경합니다.
public 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
Terry Adams
Fadi Fakhouri
Hanying Feng
Cesar Garcia
Hugo Garcia
Sven Mortensen
Svetlana Omelchenko
Lance Tucker
Michael Tucker
Eugene Zabokritski
Key: False
Debra Garcia
Claire O'Donnell
*/
다음 예제에서는 익명 형식을 사용하여 여러 값이 포함된 키를 캡슐화하는 방법을 보여 줍니다.이 예제에서 첫 번째 키 값은 학생의 성 중에서 첫 번째 문자입니다.두 번째 키 값은 학생이 첫 번째 시험에서 85점이 넘는 점수를 받았는지 여부를 지정하는 부울입니다.키의 임의 속성으로 그룹을 정렬할 수 있습니다.
다음 메서드를 StudentClass 클래스에 붙여 넣습니다.Main 메서드의 호출 문을 sc.GroupByCompositeKey()로 변경합니다.
public 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 A who scored more than 85
Terry Adams
Name starts with F who scored more than 85
Fadi Fakhouri
Hanying Feng
Name starts with G who scored more than 85
Cesar Garcia
Hugo 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 less than 85
Claire O'Donnell
Name starts with O who scored more than 85
Svetlana Omelchenko
Name starts with T who scored less than 85
Lance Tucker
Name starts with T who scored more than 85
Michael Tucker
Name starts with Z who scored more than 85
Eugene Zabokritski
*/
코드 컴파일
테스트하려는 각 메서드를 복사하여 StudentClass 클래스에 붙여 넣습니다.메서드에 대한 호출 문을 Main 메서드에 추가하고 F5 키를 누릅니다.
사용자 고유의 응용 프로그램에 이러한 메서드를 적용하는 경우 LINQ에서는 .NET Framework 버전 3.5 또는 4를 필요로 하며 프로젝트에는 System.Core.dll에 대한 참조와 System.Linq에 대한 using 지시문이 포함되어야 합니다.LINQ to SQL, LINQ to XML 및 LINQ to DataSet 형식에는 using 지시문 및 참조가 추가로 필요합니다.자세한 내용은 방법: LINQ 프로젝트 만들기를 참조하십시오.
참고 항목
작업
방법: 그룹화 작업에서 하위 쿼리 수행(C# 프로그래밍 가이드)