반응형
1. 데이터! 데이터! 데이터!
LINQ(Language INtegrated Query): C# 언어에 통합된 데이터 질의 기능
질의(Query)란
- From: 어떤 데이터 집합에서 찾을 것인가
- Where: 어떤 값의 데이터를 찾을 것인가
- Select: 어떤 항목을 추출할 것인가
class Profile
{
public string Name { get; set; }
public int Height { get; set; }
}
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 }
};
LINQ가 아닌 코드
List<Profile> profiles = new List<Profile>();
foreach (Profile profile in arrProfile) // arrProfile 안의 각 데이터
{
if (profile.Height < 175) // Height가 175 미만인 객체만
profiles.Add(profile)
}
profiles.Sort((profile1, profile2) => {
return profile1.Height - profile2.Height;
});
foreach (var profile in profiles)
Console.WriteLine($"{profile.Name} {profile.Height}");
LINQ를 이용한 코드
var profiles = from profile in arrProfile
where profile.Height < 175
orderby profile.Heigh
select profile;
foreach (var profile in profiles)
Console.WriteLine($"{profile.Name} {profile.Height}");
2. LINQ의 기본: FROM, WHERE, ORDERBY, SELECT
1. FROM
FROM의 데이터 원본은 아무 형식이나 사용할 수는 없고, IEnumerable<T> 인터페이스를 상속하는 형식이어야 함
foreach문의 반복 변수는 데이터 원본으로부터 데이터를 담아내지만, 범위 변수는 실제로 데이터를 담지는 않습니다.
따라서 쿼리식 외부에서 선언된 변수에 범위 변수의 데이터를 복사해 넣는다던가 하는 일은 할 수 없습니다.
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var result = from n in numbers // n: 범위 변수, numbers: 데이터 원본
where n % 2 == 0
order by n
select n;
2. WHERE
WHERE은 한 마디로 필터 역할을 하는 연산자입니다.
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 }
};
// 다음과 같은 WHERE 연산자를 이용해서 Height가 175 미만인 객체들을 걸러낼 수 있습니다.
var profiles = from profile in arrProfile
where profile.Height < 175
select profile;
3. ORDER BY
ORDERBY는 데이터의 정렬을 수행하는 연산자입니다.
// 다음과 같은 ORDERBY 연산자에 정렬의 기준이 되는 항목을 매개 변수로 입력해주면 됩니다.
var profiles = from profile in arrProfile
where profile.Height < 175
orderby profile.Height
select profile;
orderby profile.Height ascending // 오름차순
orderby profile.Height descending // 내림차순
4. SELECT
SELECT 절은 최종 결과를 추출하는 쿼리식의 마침표 같은 존재입니다.
// 다음과 같이 사용해주면 됩니다.
var profiles = from profile in arrProfile
where profile.Height < 175
orderby profile.Height
select profile.Name;
이렇게 사용하면 profiles는 IEnumerable<string> 형식으로 컴파일됩니다.
// 다음과 같이 무명 형식을 사용할 수도 있습니다.
var profiles = from profile in arrProfile
where profile.Height < 175
orderby profile.Height
select new{ Name = profile.Name, InchHeight = profile.Height * 0.393 };
무명 형식을 사용해서 새로운 형식을 즉석에서 만들어낼 수도 있습니다.
3. 여러 개의 데이터 원본에 질의하기
학급의 성적을 나타내는 클래스가 있고, 여러 학생의 점수를 담는 Score 필드가 배열로 있다고 가정해봅시다.
class Class
{
public string Name { get; set; }
public int[] Score { get; set; } // 배열
}
Class[] arrClass =
{
new Class() { Name="연두반", Score=new int[] { 99, 80, 70, 24 } },
new Class() { Name="분홍반", Score=new int[] { 60, 45, 87, 72 } },
new Class() { Name="파랑반", Score=new int[] { 92, 30, 85, 94 } },
new Class() { Name="노랑반", Score=new int[] { 90, 88, 0, 17 } }
};
위 배열에서 점수가 60점 미만인 학생이 소속되어 있는 학급과 그 학생의 점수를 중첩한 from 절을 이용해서 뽑아보겠습니다.
var classes = from c in arrClass // 첫 번째 데이터 원본
from s in c.Score // 두 번째 데이터 원본
where s < 60
orderby s
select new { c.Name, Lowest = s };
foreach (var c in classes)
Console.WirteLine($"낙제 : {c.Name} ({c.Lowest})");
(결과)
낙제 : 노랑반 (0)
낙제 : 노랑반 (17)
낙제 : 연두반 (24)
낙제 : 파랑반 (30)
낙제 : 분홍반 (45)
4. GROUP BY로 데이터 분류하기
group A by B into C
A 절에는 from에서 뽑아낸 범위 변수를, B에는 분류 기준을, C는 그룹 변수를 위치시키면 됩니다.
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 }
};
이 데이터를 group by를 이용해서 분류해봅시다.
var listProfile = from profile in arrProfile
group profile by profile.Height < 175 into g
select new { GroupKey = g.Key, Profiles = g };
foreach (var Group in listProfile)
{
Console.WriteLine($"- 175cm 미만? : {Group.GroupKey}");
foreach (var profile in Group.Profiles) {
Console.WriteLine($" {profile.Name}, {profile.Height}");
}
}
(결과)
- 175cm 미만? : True
김태희, 158
하하, 171
고현정, 172
- 175cm 미만? : False
이문세, 178
정우성, 186
5. 두 데이터 원본을 연결하는 JOIN
1. 내부 조인
내부 조인: 교집합과 비슷, 두 데이터 원본 사이에서 일치하는 데이터들만 연결한 후 반환
from a in A
join b in B on a.XXXX equals b.YYYY
이때 on 절의 조인 조건은 "동등"만 허용하고, 기본 연산자인 "==" 연산자가 아닌, equals라는 키워드를 사용합니다.
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
};
(결과)
이름: 정우성, 작품: 비트, 키: 186
이름: 김태희, 작품: CF 다수, 키: 158
이름: 김태희, 작품: 아이리스, 키: 158
이름: 고현정, 작품: 모래시계, 키: 172
이름: 이문세, 작품: Solo 예찬, 키: 178
2. 외부 조인
외부 조인: 기본적으로 내부 조인과 비슷하지만, 조인 결과에 기준이 되는 데이터 원본이 모두 포함됩니다.
참고) LINQ가 지원하는 외부 조인은 왼쪽 조인만을 지원합니다.
var listProfile =
from profile in arrProfile
join product in arrProduct on profile.Name equals product.Star into ps
from product in ps.DefaultEmpty(new Product() { Title="그런거 없음" })
select new
{
Name = profile.Name,
Work = product.Title,
Height = profile.Height
};
(결과)
이름: 정우성, 작품: 비트, 키: 186
이름: 김태희, 작품: CF 다수, 키: 158
이름: 김태희, 작품: 아이리스, 키: 158
이름: 고현정, 작품: 모래시계, 키: 172
이름: 이문세, 작품: Solo 예찬, 키: 178
이름: 하하, 작품: 그런거 없음, 키: 171
6. LINQ의 비밀과 LINQ 표준 연산자
컴파일러는 어떻게 LINQ를 CLR이 이해하는 코드로 만들어내는 걸까요?
var profiles = from profile in arrProfile
where profile.Height < 175
orderby profile.Height
select new { Name = profile.Name, InchHeight = profile.Height * 0.393 };
C# 컴파일러는 다음과 같은 코드로 번역합니다.
var profiles = arrProfile
.Where ( profile => profile.Height < 175 )
.OrderBy ( profile => profile.Height )
.Select ( profile => new {
Name = profile.Name,
InchHeight = profile.Height * 0.393
});
LINQ 예제에서 늘 사용해왔던 arrProfile 객체는 배열입니다. 배열은 IEnumerable<T> 의 파생 형식이며, IEnumerable<T>는 System.Collections.Generic 네임스페이스 소속입니다. Where(), OrderBy(), Select() 등의 메소드는 System.Linq 네임스페이스에 정의되어있는 IEnumerable<T>의 확장 메소드입니다. 따라서 이들을 사용하려면 System.Linq 네임스페이스를 사용하도록 선언해야합니다.
다음은 LINQ 표준 연산자입니다. 53개의 표준 LINQ 연산 메소드 중에 C#의 쿼리식에서 지원하는 것은 달랑 11개 뿐입니다. 11가지만으로도 대부분의 데이터 처리가 가능하지만, 나머지 42개를 모두 활용할 수 있다면 편하겠죠?
[C#] LINQ 표준 연산자
LINQ 표준 연산자 종류 메소드 이름 설명 C# 쿼리식 문법 정렬 Order...
blog.naver.com
LINQ 쿼리식과 위 메소드를 함께 사용해봅시다. 키가 180cm 미만인 연예인들의 키 평균을 구해볼까요.
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 }
};
double Average = (from profile in arrProfile
where profile.Height < 180
select profile).Average(profile => profile.Height);
Console.WriteLine(Average); // 169.75 출력
끝
반응형
'C# > 이것이 C#이다' 카테고리의 다른 글
14장. 람다식 (0) | 2023.05.18 |
---|---|
13장. 대리자와 이벤트 (0) | 2023.05.18 |
11장. 일반화 프로그래밍 (0) | 2023.05.18 |
10장. foreach가 가능한 객체 만들기 (0) | 2023.05.18 |
9장. 프로퍼티 (0) | 2023.05.16 |