본문 바로가기

C#/Study

[복습] LINQ - C#, 링크 ,링큐

반응형

배경

  • C# 버전 3.0에 추가된 기능
    • C# 버전 3.0은 Visual Studio 2008과 함께 2007년 말에 출시되었지만 언어 기능을 완전히 갖춘 버전은 .NET Framework 버전 3.5와 함께 제공됨

 

사용이유

private static List<Student> students = new List<Student>
{
    new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List<int> {97, 92, 81, 60}},
    new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new List<int> {75, 84, 91, 39}},
    new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List<int> {88, 94, 65, 91}},
    new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List<int> {97, 89, 85, 82}},
    new Student {First="Debra", Last="Garcia", ID=115, Scores= new List<int> {35, 72, 91, 70}},
    new Student {First="Fadi", Last="Fakhouri", ID=116, Scores= new List<int> {99, 86, 90, 94}},
    new Student {First="Hanying", Last="Feng", ID=117, Scores= new List<int> {93, 92, 80, 87}},
    new Student {First="Hugo", Last="Garcia", ID=118, Scores= new List<int> {92, 90, 83, 78}},
    new Student {First="Lance", Last="Tucker", ID=119, Scores= new List<int> {68, 79, 88, 92}},
    new Student {First="Terry", Last="Adams", ID=120, Scores= new List<int> {99, 82, 81, 79}},
    new Student {First="Eugene", Last="Zabokritski", ID=121, Scores= new List<int> {96, 85, 91, 60}},
    new Student {First="Michael", Last="Tucker", ID=122, Scores= new List<int> {94, 92, 91, 91}}
};
  • 위와 같은 List를 처리해야 하는 경우 (예 : JSON 파싱) LINQ가 필요

예제 - ID가 111 이상 114 미만인 학생 출력 (LINQ 사용 X)

List<Student> studentList = new List<Student>();

foreach (var list in students)
{
    if (list.ID >= 111 && list.ID < 114)
    {
        studentList.Add(list);
    }
}

foreach (var list in studentList)
{
    Console.WriteLine($"{list.First}'s ID : {list.ID}");
}

예제 - ID가 111 이상 114 미만인 학생 출력 (LINQ 사용 O)

var studentList = from student in students
                  where student.ID >= 111
                  where student.ID < 114
                  select student;

foreach (var list in studentList)
{
    Console.WriteLine($"{list.First}'s ID : {list.ID}");
}

결과

 

LINQ의 기본 : from, where, orderby, select

# from

  • 모든 LINQ 쿼리식은 반드시 from 절로 시작한다.
  • from의 데이터 원본은 아무 형식이나 사용할 수 없고, IEnumerable<T> 인터페이스를 상속하는 형식이어야 한다.
  • 배열, 컬렉션 객체들은 IEnumerable<T>를 상속하기 때문에 from 절의 데이터 원본으로 사용할 수 있다.
  • from <범위 변수> in <데이터 원본>의 형식으로 사용

 

LINQ의 범위 변수와 foreach 문의 반복 변수의 차이점

  • foreach 문의 반복 변수는 데이터 원본으로부터 데이터를 담아내지만, 범위 변수는 실제로 데이터를 담지는 않는다.
  • 쿼리식 외부에서 선언된 변수에 범위 변수의 데이터를 넣는 일은 할 수 없다.

 

예제 - 짝수만 출력

int[] numbers = { 9, 2, 6, 4, 5, 3, 7, 8, 1, 10 };

var result = from n in numbers
             where n % 2 == 0
             orderby n
             select n;

foreach (int n in result)
{
    Console.WriteLine($"짝수 : {n}");
}

결과

 

# where

  • from 절이 데이터 원본으로부터 뽑아낸 범위 변수가 가져야 하는 조건을 where에 작성한다.

 

# orderby

  • 데이터 정렬을 수행하는 연산자이다.
  • defalut 값은 MSSQL과 마찬가지로 오름차순(ascending)이다.
  • 내림차순은 descending을 입력해주면 된다.

 

# select

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

예제 - 짝수 ID를 가진 사람의 First NAme 출력

List<Student> students = new List<Student>
{
    new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List<int> {97, 92, 81, 60}},
    new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new List<int> {75, 84, 91, 39}},
    new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List<int> {88, 94, 65, 91}},
    new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List<int> {97, 89, 85, 82}},
    new Student {First="Debra", Last="Garcia", ID=115, Scores= new List<int> {35, 72, 91, 70}},
    new Student {First="Fadi", Last="Fakhouri", ID=116, Scores= new List<int> {99, 86, 90, 94}},
    new Student {First="Hanying", Last="Feng", ID=117, Scores= new List<int> {93, 92, 80, 87}},
    new Student {First="Hugo", Last="Garcia", ID=118, Scores= new List<int> {92, 90, 83, 78}},
    new Student {First="Lance", Last="Tucker", ID=119, Scores= new List<int> {68, 79, 88, 92}},
    new Student {First="Terry", Last="Adams", ID=120, Scores= new List<int> {99, 82, 81, 79}},
    new Student {First="Eugene", Last="Zabokritski", ID=121, Scores= new List<int> {96, 85, 91, 60}},
    new Student {First="Michael", Last="Tucker", ID=122, Scores= new List<int> {94, 92, 91, 91}}
};

var result = from student in students
             where student.ID % 2 == 0
             select student.First;

foreach (var n in result)
{
    Console.WriteLine($"결과 : {n}");
}

결과

비교

 

  • First 변수의 타입이 string이기 때문에 IEnumerable<string> 형식으로 컴파일된다.

 

예제 - 새로운 형식 생성

var result = from student in students
             where student.ID % 2 == 0
             select new { student.First, NewID = student.First + student.ID.ToString() };

foreach (var n in result)
{
    Console.WriteLine($"{n.First} : {n.NewID}");
}

결과

 

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

  • from 문을 중첩해서 사용하셔 여러 개의 데이터 원본에 접근할 수 있다.

예제 - Scores에 60 미만의 값이 있는 사람 출력

- 리스트 -

List<Student> students = new List<Student>
{
    new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List<int> {97, 92, 81, 60}},
    new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new List<int> {75, 84, 91, 39}},
    new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List<int> {88, 94, 65, 91}},
    new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List<int> {97, 89, 85, 82}},
    new Student {First="Debra", Last="Garcia", ID=115, Scores= new List<int> {35, 72, 91, 70}},
    new Student {First="Fadi", Last="Fakhouri", ID=116, Scores= new List<int> {99, 86, 90, 94}},
    new Student {First="Hanying", Last="Feng", ID=117, Scores= new List<int> {93, 92, 80, 87}},
    new Student {First="Hugo", Last="Garcia", ID=118, Scores= new List<int> {92, 90, 83, 78}},
    new Student {First="Lance", Last="Tucker", ID=119, Scores= new List<int> {68, 79, 88, 92}},
    new Student {First="Terry", Last="Adams", ID=120, Scores= new List<int> {99, 82, 81, 79}},
    new Student {First="Eugene", Last="Zabokritski", ID=121, Scores= new List<int> {96, 85, 91, 60}},
    new Student {First="Michael", Last="Tucker", ID=122, Scores= new List<int> {94, 92, 91, 91}}
};

- 코드 -

var results = from student in students
                 from score in student.Scores
                 where score < 60
              select new { student.First, Score = score };

foreach (var result in results)
{
    Console.WriteLine($"{result.First} : {result.Score}");
}

결과

 

group by로 데이터 분류

  • group by 절은 다음의 형식으로 사용된다.
group A by B into C
  • A에는 from 절에서 뽑아낸 범위 변수
  • B에는 분류 기준
  • C는 그룹 변수

예제 - Scores 평균이 85이상, 미만 분류

var results = from student in students
              group student by student.Scores.Average() >= 85 into g
              select new { GroupKey = g.Key, Profiles = g };

foreach (var group in results)
{
    Console.WriteLine($"평균 85점 이상 : {group.GroupKey}");

    foreach (var profile in group.Profiles)
    {
        Console.WriteLine($"\t{profile.First}의 평균점수 : {profile.Scores.Average()}");
    }
}

결과

 

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

내부 조인

  • 내부 조인은 교집합과 비슷하다.
  • 두 테이블의 필드를 비교할 때 '==' 연산자가 아닌 'equals' 라는 키워드를 사용한다.
  • 내부 조인은 다음의 형식으로 사용된다.
from a in A
join b in B on a.XX equals b.YY

예제

- 리스트 -

List<Student> students = new List<Student>
{
    new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List<int> {97, 92, 81, 60}},
    new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new List<int> {75, 84, 91, 39}},
    new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List<int> {88, 94, 65, 91}},
    new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List<int> {97, 89, 85, 82}},
    new Student {First="Debra", Last="Garcia", ID=115, Scores= new List<int> {35, 72, 91, 70}},
    new Student {First="Fadi", Last="Fakhouri", ID=116, Scores= new List<int> {99, 86, 90, 94}},
    new Student {First="Hanying", Last="Feng", ID=117, Scores= new List<int> {93, 92, 80, 87}},
    new Student {First="Hugo", Last="Garcia", ID=118, Scores= new List<int> {92, 90, 83, 78}},
    new Student {First="Lance", Last="Tucker", ID=119, Scores= new List<int> {68, 79, 88, 92}},
    new Student {First="Terry", Last="Adams", ID=120, Scores= new List<int> {99, 82, 81, 79}},
    new Student {First="Eugene", Last="Zabokritski", ID=121, Scores= new List<int> {96, 85, 91, 60}},
    new Student {First="Michael", Last="Tucker", ID=122, Scores= new List<int> {94, 92, 91, 91}}
};

List<StudentGrade> studentGrades = new List<StudentGrade>
{
    new StudentGrade {ID=111, Grade=1},
    new StudentGrade {ID=112, Grade=2},
    new StudentGrade {ID=114, Grade=3},
    new StudentGrade {ID=119, Grade=2},
    new StudentGrade {ID=122, Grade=3},
		new StudentGrade {ID=140, Grade=5}
};

- 코드 -

var results = from student in students
              join grade in studentGrades on student.ID equals grade.ID
              select new
              {
                  student.ID,
                  student.First,
                  grade.Grade
              };

foreach (var result in results)
{
    Console.WriteLine($"ID : {result.ID} | Name : {result.First} | Grade : {result.Grade}");
}

결과

 

외부 조인

  • 연결할 데이터 원본에 기준 데이터 원본의 데이터와 일치하는 데이터가 없다면 그 부분은 빈 값으로 결과를 채우게 된다.
  • LINQ는 LEFT JOIN만 지원한다.
  • 사용방법
    • join 절을 이용해서 조인을 수행한 후 그 결과를 임시 컬렉션에 저장한다.
    • 임시 컬렉션에 대해 DefaultIfEmpty 연산을 수행해서 비어있는 조인 결과에 빈 값을 채워 넣는다.
    • DefaultIfEmpty 연산을 거친 임시 컬렉션에서 from 절을 통해 범위 변수를 뽑아낸다.
    • 이 범위 변수와 기준 데이터 원본에서 뽑아낸 범위 변수를 이용해서 결과를 추출한다.

예제 

var results = from student in students
              join grade in studentGrades on student.ID equals grade.ID into a
              from grade in a.DefaultIfEmpty(new StudentGrade() { ID = 0 })
              select new
              {
                  student.ID,
                  student.First,
                  grade.Grade
              };

foreach (var result in results)
{
    Console.WriteLine($"ID : {result.ID} | Name : {result.First} | Grade : {result.Grade}");
}

결과

반응형