C#은 데이터를 쉽고 효율적으로 처리하기 위해 LINQ라는 것을 지원해준다.

SQL 문법을 사용해서 데이터를 간편하게 탐색하고 처리하는 것이다.

쉽게 생각하면 데이터베이스를 다루듯이 C#의 자료구조를 다룬다고 생각하면 된다.

 

먼저, 아래 한 가지 상황을 가정해보자.

int[] Arr = { 1, 7, 3, 2, 8 };

위와 같은 배열에서 5 이상의 원소만 검출하고 싶다고 해보자.

가장 단순하게 우리가 생각할 수 있는 건 아래와 같은 방식일 것이다.

List<int> SelectedNum = new List<int>();

for(int Index = 0; Index < Arr.Length; Index++ )
{
    if (Arr[Index] >= 5)
    {
        SelectedNum.Add(Arr[Index]);
    }
}

for문을 통해 배열은 순회하면서 5 이상의 원소를 모두 걸러내는 것이다.

아주 간단하다.

 

그렇다면, 이번엔 아래 상황을 가정해보자.

struct Student
{
    public Student(string name, int age, int score)
    {
        _name = name;
        _age = age;
        _score = score;
    }

    public string _name; //이름
    public int _age; // 나이
    public int _score; // 성적
}

namespace Test
{
    public class MainClass
    {
        static int Main()
        {
            Student[] Arr = { new Student( "김민수", 13, 80 ), 
                              new Student("최종훈", 18, 90), 
                              new  Student("박상민", 14, 75)};

            return 0;
        }
    }
}

 

이름, 나이, 성적을 필드로 가진 Student 구조체가 있고, 위의 코드처럼 3명의 학생이 있다고 해보자.

여기서, 점수가 80점 이상인 학생의 나이만 검출해서 그 평균값을 구하고 싶다고 해보자.

 

예를 들어, 위의 세 학생중에서 점수가 80점 이상인 학생은 김민수, 최종훈이고 그 둘의 나이는 13살, 18살이다.

두 나이의 평균값은 15.5이므로, 구하고자 하는 값은 15.5가 될 것이다.

 

이를 구하는 가장 단순한 방법은 아래와 같을 것이다.

 Student[] Arr = { new Student( "김민수", 13, 80 ), new Student("최종훈", 18, 90), new  Student("박상민", 14, 75)};

 int AgeSum = 0;
 int NumSelected = 0;

 foreach (Student student in Arr)
 {
     if(student._score >= 80)
     {
         AgeSum += student._age;
         NumSelected++;
     }
 }

 float Answer = (float)AgeSum / NumSelected;

 return 0;

Arr을 순회하면서, 점수가 80점 이상인 친구들의 나이를 AgeSum 이라는 지역변수에 모두 더한 뒤, 점수가 80점 이상인 학생들의 수로 AgeSum을 나누어 평균을 구하는 것이다.

 

첫 번째 예시에 비해, 조건이 다양해지니 조금 복잡해 진 것을 알 수 있다.

 

만약 여기보다 조건이 더 많아진다고 해보자.

예를 들어, 성별, 나이, 재산, 이름, 거주지 등 여러 필드를 가진 구조체를 원소로 보유한 자료구조에서

13살 이상인 여성의 재산 평균을 구한다든가, 경기도에 사는 20세 미만 남성의 재산 총 합을 구한다든가 이런식으로 조건이 많아질수록 식은 점점 복잡해질 것이다.

 

그렇다면, LINQ를 사용하면 어떨까?

위의 두 번째 예시를 LINQ로 한 번 작성해보겠다.

Student[] Arr = { new Student( "김민수", 13, 80 ), new Student("최종훈", 18, 90), new  Student("박상민", 14, 75)};

var Selected = from student in Arr
               where student._score >= 80
               select student._age;

float Answer = (float)Selected.Sum() / Selected.Count();

return 0;

아주 간단해졌다. 사실 위의 예제도 크게 복잡한 것은 아니라서 체감이 크게 안될 수도 있지만, LINQ를 사용하면 데이터를 단순하고 쉽게 처리할 수 있게 된다.

 

중요한 것은 아래의 구문이다.

var Selected = from student in Arr
               where student._score >= 80
               select student._age;

from이란, 자료구조의 원소를 받을 멤버 변수를 선언하는 것이다.

where이란, 자료구조의 원소 중에서 어떤 조건을 만족하는 대상을 식별할 것인지를 명시하는 것이다.

select란, 명시된 대상 중 어떤 값을 검출할 것인지를 정하는 것이다.

 

더 자세히 알아보자.

from student in Arr

Arr의 원소를 student라는 대상에 넣어서 읽겠다는 의미이다.

foreach(Student A in Arr)
{
}

위의 foreach 문과 의미가 같다.

 

where student._score >= 80

이건, _score가 80이 이상인 대상만 식별하겠다는 의미이다.

foreach(Student A in Arr)
{
    if(A._score >= 80)
    {
    }
}

위의 foreach문 내부의 if문과 의미가 같다.

 

select student._age

이는, where문을 만족한 student 중에서 그 _age를 저장하겠다는 의미이다.

List<int> Selected = new List<Student>();

foreach(Student A in Arr)
{
    if(A._score >= 80)
    {
        Selected.Add(A._age);
    }
}

위의 foreach문 내부의 if문 내부에 있는 Add구문과 의미가 동일하다.

 

var Selected = from student in Arr
               where student._score >= 80
               select student._age;

즉, 위의 구문을 정리해보자면 Arr 내부에 있는 원소중 성적이 80 이상인 학생들의 나이를 Selected에 저장하겠다는 의미이다.

float Answer = (float)Selected.Sum() / Selected.Count();

이후, Selected의 Sum 메서드를 통해 멤버함수의 합을 구한 뒤, Selected의 원소 개수로 나누면 구하고자 했던 값을 구할 수 있게 된다.

 

이 외에도, LINQ의 쿼리문은 검출된 대상을 정렬할 수도 있다. 일반적으로 LINQ의 쿼리문은 첫 번째 원소부터 순회하며 데이터를 판별하기 때문에, Selected에는 조건을 만족하는 원소 중 앞에 있는 원소부터 순차적으로 저장될 것이다. 

 

var Selected = from student in Arr
               where student._score >= 80
               orderby student._name
               select student._age;

위의 구문을 보면 아까는 없던 orderby라는 쿼리문이 추가되었다.

이는 정렬 기준을 선택하는 것이다. 위의 구문에선 student._name을 orderby 뒤에 명시했기 때문에, 저장되는 _age는 _name이 사전순으로 앞에 오는 student의 _age가 먼저 저장되도록 정렬될 것이다. 

 

만약, 저장되는 나이를 그대로 오름차순으로 저장하고 싶다면, orderby 뒤에 student._age를 명시하면 된다.

또한, 내림차순으로 정렬하고 싶다면, orderby (정렬기준) 뒤에 descending 을 붙여주면 된다.

var Selected = from student in Arr
               where student._score >= 80
               orderby student._name descending
               select student._age;

위와 같이 descending을 붙이면 내림차순으로 정렬이 된다.

즉 위의 구문을 해석해보면 이와 같다.

 

Arr에 있는 원소중, _score이 80인 대상을 검출한 뒤, 검출된 대상들을 _name을 기준으로 내림차순 정렬하고, 정렬된 상태에서 _age를 순차적으로 검출하겠다는 것이다.

 

이는 매우 기본적인 LINQ 문법이며, 실제로는 이보다 더 다양한 문법이 제공된다.

쿼리문의 종류도 더 있고, 람다함수를 활용해 메서드로 질의하는 방법도 있다.

이는 다음 게시물에서 알아보도록 하자.

'C# > C#' 카테고리의 다른 글

C# - ref, out  (0) 2024.08.13
C# - const, readonly  (0) 2024.08.10
C# - is, as 연산자  (0) 2024.08.07
C# - 클래스, 접근 제한 지정자, 프로퍼티  (0) 2024.07.27
C# - C#의 자료형  (0) 2024.07.24

+ Recent posts