C# LINQ에서 Select와 SelectMany는 모두 프로젝션(Projection) 연산자 범주에 속하지만, 결과를 반환하는 방식에서 중요한 차이가 있습니다.
Select 연산자란?
Select는 소스 시퀀스(source sequence)의 각 요소를 하나의 결과 값으로 변환(투영)합니다. 즉, 입력 요소 하나당 출력 요소 하나가 대응되며, 컬렉션 안에 컬렉션이 있는 구조라면 그대로 중첩된 형태로 유지됩니다.
SelectMany 연산자란?
SelectMany는 시퀀스의 각 요소를 IEnumerable<T>로 투영한 뒤, 그 결과로 만들어진 여러 시퀀스를 하나의 평면적인(flat) 시퀀스로 병합합니다. 중첩된 컬렉션을 단일 컬렉션으로 펼쳐야 할 때 매우 유용합니다.
예제 코드
아래 예제는 제품 카테고리와 해당 브랜드 목록을 담은 데이터를 Select와 SelectMany로 각각 처리한 결과를 비교합니다.
class Demo
{
public string Name { get; set; }
public List<string> Contents { get; set; }
public static List<Demo> GetAllContents()
{
List<Demo> listContents = new List<Demo>
{
new Demo
{
Name = "Cap",
Contents = new List<string> { "Nike", "Adidas" }
},
new Demo
{
Name = "Shoes",
Contents = new List<string> { "Nike", "Puma", "Adidas" }
},
};
return listContents;
}
}
class Program
{
static void Main()
{
// Select: List<List<string>> 형태의 중첩 구조 유지
IEnumerable<List<string>> result = Demo.GetAllContents().Select(s => s.Contents);
foreach (List<string> stringList in result)
{
foreach (string str in stringList)
{
Console.WriteLine(str);
}
}
Console.WriteLine("---Select Many---");
// SelectMany: 중첩된 목록을 하나의 시퀀스로 평탄화
IEnumerable<string> resultSelectMany = Demo.GetAllContents().SelectMany(s => s.Contents);
foreach (string str in resultSelectMany)
{
Console.WriteLine(str);
}
Console.ReadKey();
}
}실행 결과
Nike Adidas Nike Puma Adidas ---Select Many--- Nike Adidas Nike Puma Adidas
핵심 차이점 요약
- Select: 각 요소를 변환하여
IEnumerable<List<string>>처럼 중첩된 구조를 그대로 반환합니다. 내부 컬렉션을 순회하려면 이중 foreach문이 필요합니다. - SelectMany: 중첩된 컬렉션을 자동으로 평탄화하여
IEnumerable<string>처럼 단일 시퀀스를 반환합니다. 하나의 foreach문으로 바로 순회할 수 있습니다.
정리하면, 요소별 변환이 필요하면 Select를, 중첩된 컬렉션을 하나로 합쳐야 한다면 SelectMany를 사용하는 것이 올바른 선택입니다.