group clause
group clause (C# 참조)
group 절은 xref:System.Linq.IGrouping`2 개체의 시퀀스를 반환해요. 이 시퀀스에는 각 그룹의 키 값과 일치하는 항목이 0개 이상 담겨요. 예를 들어 문자열 시퀀스를 각 문자열의 첫 글자를 기준으로 그룹으로 묶을 수 있죠. 이때 첫 글자가 바로 키가 되고, 타입은 char예요. 각 xref:System.Linq.IGrouping`2 개체는 이 키를 자신의 Key 속성에 저장하며, 키의 타입은 컴파일러가 추론해요.
[!INCLUDEcsharp-version-note]
쿼리 식은 다음 예시처럼 group 절로 끝낼 수도 있어요.
:::code language="csharp" source="./snippets/group.cs" id="10":::
각 그룹에 추가 쿼리 작업을 더 수행하고 싶다면 into 상황별 키워드로 임시 식별자를 지정하면 돼요. into를 사용하면 반드시 쿼리를 이어서 작성해야 하고, 결국엔 select 문이나 또 다른 group 절로 끝내야 해요. 다음 발췌가 그 모습이에요.
:::code language="csharp" source="./snippets/group.cs" id="11":::
into를 쓸 때와 쓰지 않을 때 group의 더 완전한 예시는 이 문서의 Example 섹션에서 확인하실 수 있어요.
그룹 쿼리 결과 열거하기
group 쿼리가 만들어 내는 xref:System.Linq.IGrouping`2 개체는 실질적으로 리스트의 리스트라서, 각 그룹 안의 항목에 접근하려면 중첩된 foreach 루프를 써야 해요. 바깥 루프는 그룹 키를, 안쪽 루프는 그룹 자체의 각 항목을 반복하죠. 그룹은 키는 있는데 요소가 아예 없을 수도 있어요. 다음 foreach 루프가 앞선 코드 예시의 쿼리를 실행해요.
:::code language="csharp" source="./snippets/group.cs" id="12":::
키 타입
그룹 키는 어떤 타입이든 될 수 있어요. 문자열, 기본 제공 숫자 타입, 사용자 정의 명명 타입, 익명 타입 모두 괜찮아요.
문자열로 그룹화하기
앞선 코드 예시에서는 char를 썼죠. 완전한 성(last name)처럼 문자열 키를 지정할 수도 있어요.
:::code language="csharp" source="./snippets/group.cs" id="13":::
bool로 그룹화하기
다음 예시는 키로 bool 값을 사용해 결과를 두 그룹으로 나눠요. 그 값은 group 절 안의 하위 식에서 나와요.
:::code language="csharp" source="./snippets/group.cs" id="14":::
숫자 범위로 그룹화하기
다음 예시는 백분위 범위를 나타내는 숫자 그룹 키를 식으로 만들어요. let로 메서드 호출 결과를 저장해서, group 절에서 같은 메서드를 두 번 호출하지 않도록 하죠. 쿼리 식에서 메서드를 안전하게 사용하는 자세한 방법은 쿼리 식에서 예외 처리하기를 참고해요.
:::code language="csharp" source="./snippets/group.cs" id="15":::
복합 키로 그룹화하기
둘 이상의 키로 요소를 묶고 싶다면 복합 키를 사용해요. 익명 타입이나 명명 타입을 써서 키 요소를 담으면 복합 키가 만들어져요. 다음 예시에서 Person 클래스에 surname과 city라는 멤버가 있다고 가정할게요. group 절은 성과 도시가 모두 같은 사람들의 집합마다 별도의 그룹을 만들어요.
group person by new {name = person.surname, city = person.city};
쿼리 변수를 다른 메서드에 전달해야 한다면 명명 타입을 사용하세요. 키를 위한 자동 구현 속성을 가진 특수 클래스를 만들고, xref:System.Object.Equals*와 xref:System.Object.GetHashCode* 메서드를 재정의하면 돼요. struct를 사용해도 되는데, 이 경우엔 그런 메서드 재정의가 엄격히 요구되진 않아요. 자세한 내용은 자동 구현 속성을 가진 경량 클래스 구현 방법과 디렉터리 트리에서 중복 파일 쿼리하기를 참고해요. 후자의 글에는 명명 타입으로 복합 키를 쓰는 방법을 보여주는 코드 예시가 있어요.
예시
다음 예시는 그룹에 추가 쿼리 로직을 적용하지 않고 원본 데이터를 그룹으로 정렬하는 표준 패턴을 보여줘요. 이 패턴을 '연속 없이 그룹화(grouping without a continuation)'라고 불러요. 이 예시는 문자열 배열의 요소를 첫 글자에 따라 그룹으로 묶죠. 쿼리 결과는 char 타입의 공개 Key 속성과 그룹의 각 항목을 담은 xref:System.Collections.Generic.IEnumerable`1 컬렉션을 가진 xref:System.Linq.IGrouping`2 타입이에요.
group 절의 결과는 시퀀스의 시퀀스예요. 각 반환 그룹 안의 개별 요소에 접근하려면, 그룹 키를 반복하는 바깥 루프 안에 중첩 foreach 루프를 쓰면 돼요. 다음 예시가 그 모습이에요.
:::code language="csharp" source="./snippets/group.cs" id="16":::
다음 예시는 그룹을 만든 뒤 추가 로직을 수행하는 방법을 보여줘요. into로 *연속(continuation)*을 사용하는 방식이에요. 자세한 내용은 into를 참고하세요. 이 예시는 각 그룹을 쿼리해서 키 값이 모음인 그룹만 고릅니다.
:::code language="csharp" source="./snippets/group.cs" id="17":::
컴파일 타임에 컴파일러는 group 절을 xref:System.Linq.Enumerable.GroupBy* 메서드 호출로 변환해요.
group 절 쿼리의 구문은 사용자 지정 동등 비교자(equality comparer)를 지원하지 않아요. 쿼리에서 xref:System.Collections.IEqualityComparer를 사용하고 싶다면 xref:System.Linq.Enumerable.GroupBy* 메서드를 명시적으로 사용하세요.