LINQ

개발조하·2023년 11월 10일
0

C#

목록 보기
9/11
post-thumbnail

1. LINQ란?

컬렉션을 편리하게 다루기 위한 목적으로 만들어진 데이터 질의(Query)언어이다.
데이터 질의라고 하면 데이터에 대해 물어본다는 의미로 그 질문을 받은 누군가는 데이터에 대한 답변을 준다는 것이다.
여기에서 질문은 다음 내용을 포함한다.

From: 어떤 데이터 집합에서 찾을 것인가?
Where: 어떤 값의 데이터를 찾을 것인가?(조건)
Select: 어떤 항목을 추출할 것인가?

  • 예시: 짝수 구하기

1.1 from

모든 LINQ 쿼리식은 반드시 from절로 시작한다. from절로 데이터 원본과 범위 변수를 지정해줘야 한다

  • 범위 변수: foreach x in arr foreach문에서 x와 같은 반복 변수를 뜻한다.
    ㄴ LINQ의 범위 변수와 foreach문의 반복 변수의 차이점: foreach문의 반복 변수는 데이터 원본의 데이터를 담아내지만, 범위 변수는 실제로 데이터를 담지는 않는다. 쿼리식 외부에서 선언된 변수에 범위 변수의 데이터를 복사해 넣는다든가 하는 일은 할 수 없다. 오로지 LINQ 질의 안에서만 통용되며, 질의가 실행될 때 어떤 일이 일어날지를 묘사하기 위해 도입된 것이다.

from의 데이터 원본은 꼭 IEnumerable<T> 인터페이스를 상속하는 형식이어야 한다.
from절로 데이터 원본으로부터 범위 변수를 뽑어낸 후에는 LINQ가 제공하는 수십 가지 연산자(where, orderby, select ...)를 이용해서 데이터를 가공 및 추출할 수 있다.

1.2 where

필터(filter)역할을 하는 연산자. from절이 데이터 원본으로부터 뽑아낸 범위 변수가 가져야 하는 조건.
위 코드를 보면 where n % 2 == 0을 통해 나눴을 때 0이 되는 수, 즉 짝수의 조건을 넣어줬다.

1.3 orderby

데이터의 정렬을 수행하는 연산자.
기본 디폴트는 ascending 오름차순이지만, orderby n ascending이렇게 명시해줘도 된다. 반대로 내림차순은 orderby n descending으로 명시한다.

1.4 select

최종 결과를 추출하는 쿼리식의 마침표 같은 존재. var 형식으로 선언된 result의 실제 형식은 어떻게 될까? LINQ의 질의 결과는 IEnumerable<T>로 반환되는데, 이때 형식 매개변수 T는 바로 select문에 의해 결정된다. 위 코드에서는 IEnumerable<n>이 되는 것이다.

  • 무명 형식의 이용
    select문은 무명 형식을 이용해서 새로운 형식을 즉석에서 만들 수도 있다.

2. 여러 개의 데이터 원본에 질의하기

여러 개의 데이터 원본에 접근하려면 from문을 중첩해서 사용한다.

3. group by로 데이터 분류하기

분류 기준에 따라 데이터를 그룹화 해주는 방법

  • 형식

    group 범위변수 by 분류기준 into 그룹변수

4. 두 데이터 원본을 연결하는 join

각 데이터 원본에서 특정 필드의 값을 비교하여 일치하는 데이터끼리 연결한다.

4.1 내부 조인

= 교집합
두 데이터 원본 사이에서 일치하는 데이터들만 연결한 후 반환한다.

4.2 외부 조인

기준이 되는 데이터 원본의 모든 데이터를 조인 결과에 반드시 포함시킨다. 연결할 데이터 원본에 기준 데이터 원본의 데이터와 일치하는 데이터가 없다면 그 부분은 빈 값으로 결과를 채운다.

  • LINQ 외부 조인에서는 왼쪽 조인 즉, 왼쪽 데이터 원본을 기준으로 삼는다.

4.3 내부 조인 및 외부 조인 예시

using System;
using System.Linq;

namespace LambdaTest
{
    class Profile 
    {
        public string Name { get; set; }
        public int Height { get; set; }
    }

    class Product
    {
        public string Title { get; set; }
        public string Star { get; set; }
    }

    internal class Program
    {
        static void Main(string[] args)
        {
            Profile[] arrProfile =
            {
                new Profile(){Name = "정우성", Height = 186},
                new Profile(){Name = "김태희", Height = 158},
                new Profile(){Name = "고현정", Height = 172},
                new Profile(){Name = "이문세", Height = 178},
                new Profile(){Name = "하하", Height = 171},
            };

            Product[] arrProduct = 
            {
                new Product(){Title = "비트", Star = "정우성"},
                new Product(){Title = "CF 다수", Star = "김태희"},
                new Product(){Title = "아이리스", Star = "김태희"},
                new Product(){Title = "모래시계", Star = "고현정"},
                new Product(){Title = "Solo 예찬", Star = "이문세"},
            };

            //내부 조인
            var listProfile = from profile in arrProfile
                              join product in arrProduct on profile.Name equals product.Star
                              select new
                              {
                                  Name = profile.Name,
                                  Work = product.Title,
                                  Height = profile.Height
                              };

            Console.WriteLine(" ---- 내부 조인 결과 ----");
            foreach (var profile in listProfile)
            {
                Console.WriteLine($"이름: {profile.Name}, 작품: {profile.Work}, 키: {profile.Height}");
            }

            //외부 조인
            listProfile = from profile in arrProfile
                          join product in arrProduct on profile.Name equals product.Star into ps
                          from product in ps.DefaultIfEmpty(new Product() { Title = "없음" })
                          select new
                          {
                              Name = profile.Name,
                              Work = product.Title,
                              Height = profile.Height
                          };

            Console.WriteLine();
            Console.WriteLine(" ---- 외부 조인 결과 ----");
            foreach (var profile in listProfile)
            {
                Console.WriteLine($"이름: {profile.Name}, 작품: {profile.Work}, 키: {profile.Height}");
            }

        }
    }
}

5. 연습문제

출처: [이것이 C#이다 3판(박상현)] 15장 연습문제

  1. 다음과 같은 배열이 있다고 할 때, Cost는 50 이상, MaxSpeed는 150 이상인 레코드만 조회하는 LINQ를 작성하세요.
  • 풀이
namespace LambdaTest
{
    class Car
    {
        public int Cost { get; set; }
        public int MaxSpeed { get; set; }
    }

    internal class Program
    {
        static void Main(string[] args)
        {
            Car[] car =
            {
                new Car(){Cost = 56, MaxSpeed = 120},
                new Car(){Cost = 70, MaxSpeed = 150},
                new Car(){Cost = 45, MaxSpeed = 180},
                new Car(){Cost = 32, MaxSpeed = 200},
                new Car(){Cost = 82, MaxSpeed = 280},
            };

            var selected = from c in car
                           where c.Cost >= 50 && c.MaxSpeed >= 150
                           select c;

            foreach(var c in selected)
            {
                Console.WriteLine($"cost = {c.Cost}, MaxSpeed = {c.MaxSpeed}");
            }
        }
    }
}
  1. 다음 코드에서 cars.Where(c => c.Cost < 60).OrderBy(c => c.Count)와 동일한 결과를 반환하는 LINQ를 작성하세요.
namespace LambdaTest
{
    class Car
    {
        public int Cost { get; set; }
        public int MaxSpeed { get; set; }
    }

    internal class Program
    {
        static void Main(string[] args)
        {
            Car[] cars =
            {
                new Car(){Cost = 56, MaxSpeed = 120},
                new Car(){Cost = 70, MaxSpeed = 150},
                new Car(){Cost = 45, MaxSpeed = 180},
                new Car(){Cost = 32, MaxSpeed = 200},
                new Car(){Cost = 82, MaxSpeed = 280},
            };

            var selected =
                //cars.Where(c => c.Cost < 60).OrderBy(c => c.Cost);
                from car in cars
                where car.Cost < 60
                orderby car.Cost
                select car;

            foreach (var c in selected)
            {
                Console.WriteLine($"cost = {c.Cost}, MaxSpeed = {c.MaxSpeed}");
            }
        }
    }
}

📄참고자료
<이것이 c#이다> 3판 - 박상현 지음 (한빛미디어)
[MSDN_LINQ]

profile
Unity 개발자 취준생의 개발로그, Slow and steady wins the race !

0개의 댓글