group 절(C# 참조)
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};
쿼리 변수를 다른 메서드로 전달해야 하는 경우 명명된 형식을 사용합니다.키에 대해 자동 구현된 속성을 사용하여 특수 클래스를 만든 다음 Equals 및 GetHashCode 메서드를 재정의합니다.이러한 메서드를 반드시 재정의할 필요가 없는 구조체를 사용할 수도 있습니다.자세한 내용은 방법: 자동으로 구현된 속성을 사용하여 간단한 클래스 구현(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# 프로그래밍 가이드)