5 분 소요

이 글은 한빛미디어의 이것이 C#이다를 보고 헷갈리는 부분 위주로 정리한 글입니다.

LINQ

C#의 LINQ(Language Integrated Query)는 데이터에 대한 질의를 SQL처럼 간단하게 작성하도록 도와주는 기능이다.

데이터 질의 : 데이터에 대해 물어본다. 즉, 어떠한 데이터들을 물어보고 답변을 받는다.

다음과 같은 키워드들을 통해 질문을 할 수 있다. From : 어떤 데이터 집합에서 찾을 것인가? Where : 어떤 값의 데이터를 찾을 것인가? Select : 어떤 항목을 추출할 것인가.

class Man
{
    public int age = 5;
    public string name = "name"; 
}
class Program
{
    static void Main(string[] args)
    {
        Man[] mans = {
            new Man() {age = 5 ,name = "김철수"},
            new Man() {age = 25 ,name = "방민호"},
            new Man() {age = 20 ,name = "김태희"},
        };

        // LINQ
        var newMans = from man in mans // mans 안에 있는 각 데이터로부터
                       where man.age > 6 // age가 6보다 큰 데이터를 골라
                       orderby man.age // age순으로 정렬하여
                       select man; // 객체 추출

        foreach (Man man in newMans)
        {
            Console.WriteLine(man.name);
        }
    }
}

// 결과
// 김태희
// 방민호

LINQ의 기본

from절

모든 LINQ 쿼리식은 반드시 from으로 시작해야한다. 쿼리식의 대상이 될 데이터 원본과 데이터 원본 안에 들어있는 각 요소 데이터를 나타내는 범위 변수from절에서 지정해줘야 한다.
이때 데이터 원본은 IEnumerable<T> 인터페이스를 상속받는 형식이여야만 한다.
IEnumeable를 상속받는다는 것은 컬력센이나 배열같은 `데이터 집합임을 의미` 한다.
즉, 일반적인 컬렉션, 배열 등은 물론 가능하고 직접 IEnumerable을 상속해서 직접 만든 데이터 집합도 가능하다.

LINQ의 범위 변수와 foreach문의 반복 변수의 차이점 : foreach문의 반복 변수는 데이터 원본으로부터 데이터를 담아내지만, 범위 변수는 실제로 데이터를 담지는 않는다. 그래서 쿼리식 외부에서 선언된 변수에 범위 변수의 데이터를 복사해 넣는 다는가 하는 일은 할 수 없음.


from절은 다음과 같이 from 범위변수 in 데이터원본 의 형식으로 사용한다.


int[]  num = { 1,2,3,4,5,6};

var result = from n in num // 범위변수 n , 데이터 원본 num
             where n > 5
             orderby n
             select n;

where

where은 한마디로 필터 역할을 하는 연산자이다. from 절이 데이터 원본으로부터 뽑아낸 범위 변수가 가져야 하는 조건을 where 연산자에 인수로 입력하면 LINQ는 해당 조건에 부합하는 데이터만을 걸러냄.

다음과 같은 데이터가 있다.

Man[] mans = {
    new Man() {age = 5 ,name = "김철수"},
    new Man() {age = 25 ,name = "방민호"},
    new Man() {age = 20 ,name = "김태희"},
};


이러한 데이터를 where문을 이용하여 age가 6 이상인 데이터만 걸러낼 수 있다.

var newMans = from man in mans 
              where man.age > 6 // age가 6보다 큰 데이터를 걸러냄
              orderby man.age 
              select man; 

orderby

orderby는 데이터의 정렬을 수행하는 연산자이다. 바로 앞에서 where의 예제에 있는 orderby를 그대로 보면 age를 기준으로 오름차순 정렬을 함을 알 수 있다.

var newMans = from man in mans 
              where man.age > 6 
              orderby man.age // age 기준으로 오름차순 정렬
              select man; 

기본적으로 오름차순 정렬이 기본이지만 명확하게 하기위해 또는 내림차순 정렬을 하기 위해 키워드를 명시할 수 있다.

var newMans = from man in mans 
              where man.age > 6 
              orderby man.age ascending // 내림자순은 descending
              select man; 

select

select 절은 최종 결과를 추출하는 쿼리식의 마침표같은 존재이다. LINQ의 질의 결과는 IEnumerable로 반환되는데, 이때 형식 매개변수 T는 select문에 의해서 결정된다.

Man[] mans = {
    new Man() {age = 5 ,name = "김철수"},
    new Man() {age = 25 ,name = "방민호"},
    new Man() {age = 20 ,name = "김태희"},
};

var newMans = from man in mans 
              where man.age > 6 
              orderby man.age ascending 
              select man; //IEnumerable<Man> 
              //select man.age 라면 IEnumerable<int> select man.name 이라면 IEnumerable<string>


이뿐 아니라 select문은 무면 형식을 이용해서 즉석에서 새로운 형식을 만들 수도 있다.

var newMans = from man in mans 
              where man.age > 6 
              orderby man.age ascending // 내림자순은 descending
              select new {Name = man.name, Age = man.age}; 

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

여러 개의 데티어 원본에 접근하기 위해 from절을 중첩하여 사용한다.

class Class
{
    public int[] score;
    public string name = "name"; 
}

class Program
{
    static void Main(string[] args)
    {

        Class[] arrClass =
        {
            new Class(){name = "1반", score = new int[]{ 26,56,23,45} },
            new Class(){name = "2반", score = new int[]{ 80,90,97,85} },
            new Class(){name = "3반", score = new int[]{ 55,88,60,40} },
            new Class(){name = "4반", score = new int[]{95,20,100,99} }
        };



        var goodClasses =   from c in arrClass
                                from s in c.score   // c안에 score에 접근
                                where s > 80        // s가 80이상이면
                            orderby s descending
                            select new{name = c.name, score = s}; // 조건에 맞는 데이터의 name과 score 추출

        foreach (var c in goodClasses)
        {
            Console.WriteLine($"반 {c.name}, 점수 : {c.score}");
        }


    }
}

// 결과
//반 4반, 점수 : 100
//반 4반, 점수 : 99
//반 2반, 점수 : 97
//반 4반, 점수 : 95
//반 2반, 점수 : 90
//반 3반, 점수 : 88
//반 2반, 점수 : 85

group by로 데이터 분류하기

group by 절은 다음의 형식으로 사용함

group A by B into C

A에는 from절에서 뽑아낸 범위 변수를, B에는 분류 기준을, C에는 그룹 변수를 위치시키면 된다.

다음은 예시이다.

class Man
{
    public int age;
    public string name; 
}
class Program
{
    static void Main(string[] args)
    {
        Man[] mans = {
            new Man() {age = 5 ,name = "김철수"},
            new Man() {age = 25 ,name = "방민호"},
            new Man() {age = 20 ,name = "김태희"},
            new Man() {age = 25 ,name = "정우성"},
        };


        var newMans = from man in mans
                      orderby man.age
                      group man by man.age > 19 into g 
                      select new {GroupKey = g.Key, mans = g};
                      // GroupKey로 분류 조건에 따른 true나 false를, mans로 그룹변수를 추출한다.

        foreach (var Group in newMans)
        {
            Console.WriteLine(Group.GroupKey ? "성인" : "미성년자");

            foreach (var g in Group.mans)
            {
                Console.WriteLine($">>> {g.name}, {g.age}");
            }

        }
    }
}
// 결과
//미성년자
//>>> 김철수, 5
//성인
//>>> 김태희, 20
//>>> 방민호, 25
//>>> 정우성, 25

설명 그림

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

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

내부 조인

첫 번째 데이터 원본과 두 번쨰 데이터 원본의 특정 필드를 비교해서 일치하는 데이터를 반환. 이떄 기준은 첫 번째 원본 데이터이다.

from a in A
join b in B on a.XXXX equals b.YYYY

a는 from 절에서 뽑아낸 범위 변수, 연결 대상 데이터 b 는 join 절에서 뽑아낸 변수이다. join 절의 on 키워드는 조인 조건을 수반한다. 이때 on 절의 조건은 동등 Equality만 허용된다. ~보다 작음, ~보다 큼 같은 비교 연산은 불가능하다. 또한 == 연산자가 아닌 equals 라는 키워드를 이용한다.

태그:

카테고리:

업데이트:

댓글남기기