본문 바로가기

프로그래밍/C#

[C#] LINQ 프로젝션 작업, 표준 쿼리 연산자

반응형

표준 쿼리 연산자는 LINQ 패턴을 형성하는 메서드입니다.

프로젝션 작업은 이후에 사용할 속성으로만 구성된 새 양식으로 개체를 변환하는 작업을 가리킵니다.

예를 들어 문자열의 배열로 이루어진 개체가 있다고 하고

여기서 첫글자만 뽑아서 활용하고 싶을 때, 이 개체의 문자열에서 첫글자만을 남기는

새로운 양식으로 변환하는 작업을 생각하시면 됩니다.

이러한 작업을 프로젝션이라 하며, LINQ에서는 프로젝션 작업을 위해 'Select''SelectMany'를 지원하고 있습니다. Select의 경우 모든 소스 값에 대해 하나의 결과 값을 생성합니다. 따라서 전체 소스 컬렉션과 동일한 개수의 요소가 들어 있는 컬렉션을 만듭니다. 반면, SelectMany는 각 소스 값에서 연결된 하위 컬렉션을 포함하는 하나의 결과를 생성합니다.

 


Select

- 특정 양식을 새로운 양식으로 변환하여 저장하는 메서드입니다.

- 소스의 요소의 개수와 출력값의 요소의 개수가 같습니다.

- 이 메서드는 '지연된 실행' 입니다.

 

 

소스코드


string[] fruits = { "apple", "banana", "mango", "orange", 
                      "passionfruit", "grape" };

var query =
    fruits.Select((fruit, index) =>
                      new { index, str = fruit.Substring(0, index) });

foreach (var obj in query)
{
    Console.WriteLine("{0}", obj);
}

 

실행결과


 {index=0, str=}
 {index=1, str=b}
 {index=2, str=ma}
 {index=3, str=ora}
 {index=4, str=pass}
 {index=5, str=grape}

이 소스는 fruits 문자열을 이용하여 한 요소의 index값 만큼의 문자만을 뽑아 프로젝션하는 예제입니다.

query에 <int, str> 형태의 익명 형식을 만들어 값을 저장하고 출력하였습니다.

 

 

 

Enumerable.Select 메서드 (System.Linq)

시퀀스의 각 요소를 새 폼에 투영합니다.Projects each element of a sequence into a new form.

docs.microsoft.com

 

 

 

 

SelectMany

- 각 소스 값에서 연결된 하위 컬렉션을 포함하는 하나의 결과를 생성합니다.

- 시퀀스에 있는 각 요소를 IEnumerable<T>로 사상하고 그 결과로 나온 시퀀스를 하나의 시퀀스로 평탄화합니다.

- SQL의 '교차 조인'과 유사하게 사용할 수 있습니다.

- 다차원의 Select 쿼리 루프가 필요한 경우 이것을 SelectMany로 축소해서 사용할 수 있습니다.

- 이 메서드는 '지연된 실행' 입니다.

소스코드


List<string> animals = new List<string>() { "cat", "dog", "donkey" };
List<int> number = new List<int>() { 10, 20 };

var mix = number.SelectMany(num => animals, (n, a) => new { n, a });

foreach (var obj in mix)
    Console.WriteLine(obj);

 

실행결과


n = 10 , a = cat
n = 10 , a = dog
n = 10 , a = Monkey
n = 20 , a = cat
n = 20 , a = dog
n = 20 , a = Monkey

 

SQL의 교차 조인과 유사한 방식의 SelectMany 활용법입니다.

두 데이터 세트의 요소로 만들 수 있는 모든 조합을 만들어서 mix에 저장하고,

그것을 foreach문으로 출력한 모습입니다.

소스코드


               // Query using SelectMany().
               IEnumerable<string> query1 = petOwners.SelectMany(petOwner => petOwner.Pets);

               Console.WriteLine("Using SelectMany():");

               // Only one foreach loop is required to iterate 
               // through the results since it is a
               // one-dimensional collection.
               foreach (string pet in query1)
               {
                   Console.WriteLine(pet);
               }

               // This code shows how to use Select() 
               // instead of SelectMany().
               IEnumerable<List<String>> query2 =
                   petOwners.Select(petOwner => petOwner.Pets);

               Console.WriteLine("\nUsing Select():");

               // Notice that two foreach loops are required to 
               // iterate through the results
               // because the query returns a collection of arrays.
               foreach (List<String> petList in query2)
               {
                   foreach (string pet in petList)
                   {
                       Console.WriteLine(pet);
                   }
                   Console.WriteLine();
               }

 

두번째 예제입니다. SelectMany를 사용하면 Select만 사용한 코드보다 더 쉽게 작성할 수도 있습니다.

 

 

 

Enumerable.SelectMany 메서드 (System.Linq)

시퀀스의 각 요소를 에 투영하고 결과 시퀀스를 단일 시퀀스로 평면화합니다.Projects each element of a sequence to an and flattens the resulting sequences into one sequence.

docs.microsoft.com

 

 

심화

- MSDN 예제입니다.

소스코드


class PetOwner
        {
            public string Name { get; set; }
            public List<string> Pets { get; set; }
        }

        static void Main(string[] args)
        {
            PetOwner[] petOwners = { 
                new PetOwner { Name="Higa",
                    Pets = new List<string>{ "Scruffy", "Sam" } },
                new PetOwner { Name="Ashkenazi",
                    Pets = new List<string>{ "Walker", "Sugar" } },
                new PetOwner { Name="Price",
                    Pets = new List<string>{ "Scratches", "Diesel" } },
                new PetOwner { Name="Hines",
                    Pets = new List<string>{ "Dusty" } } };

            // Project the pet owner's name and the pet's name.
            var query =
                petOwners
                .SelectMany(petOwner => petOwner.Pets, (petOwner, petName) 
                    => new { petOwner, petName })
                .Where(ownerAndPet => ownerAndPet.petName.StartsWith("S"))
                .Select(ownerAndPet =>
                        new
                        {
                            Owner = ownerAndPet.petOwner.Name,
                            Pet = ownerAndPet.petName
                        }
                );

            // Print the results.
            foreach (var obj in query)
                Console.WriteLine(obj);
        }

 

실행결과


Owner = Higa, Pet = Scruffy
Owner = Higa, Pet = Sam
Owner = Ashkenazi, Pet = Sugar
Owner = Price, Pet = Scratches

 

 

여기까지 보고계신 분이라면 기본적인것은 다 알것이라 가정하고 표준 쿼리 연산자가 들어가는 부분부터 설명해보도록 하겠습니다. ( 소스코드의 var query 부분입니다. )

SelectMany 메서드

먼저 LINQ의 SelectMany문으로 petOwner 클래스의 요소들을 아래 형태의 익명 형식을 만들어 평탄화합니다.

( petOwner<PetOwners>, petName<string> )

즉, 이 부분에서 Pet의 이름을 기준으로 ( 동물주인, 동물이름 ) 으로 이루어진 7개의 데이터 묶음이 출력됩니다.

Where 메서드

위에서 생성된 7개의 데이터 묶음에서 동물의 이름이 'S'로 시작되냐를 기준으로 데이터를 필터링합니다.

여기서 S로 시작하지 않는 'Walker', 'Diesel', 'Dusty' 이 세 동물들은 제외됩니다.

Select 메서드

위에서 필터링된 데이터에서 주인의 이름<str>과 동물의 이름<str>만을 추출해 query에 순서대로 저장합니다.

이후 이 데이터를 출력하면 '실행결과'와 같은 출력이 나오게 됩니다.

역시 표준 쿼리 연산자의 꽃은 메서드간의 연결이라 생각합니다.

이렇게 여러개의 메서드를 연결해서 공부하니까 더 재미있네요

 

 

반응형