외로운 Nova의 작업실
c# 언어 공부 - 10(LINQ) 본문
안녕하세요. 오늘은 저번시간에 이어서 c#의 LINQ에대해서 배워보도록 하겠습니다.
LINQ는 Language INtegrated Query의 약자입니다.
한국어로 직역하면 언어 통합 질의입니다.
의역하면 데이터 질의 기능이라고 할수 있습니다.
데이터를 한곳에 모아놓고 원하는 데이터를 질의하듯이 골라 낼 수 있는 기능을 가집니다.
백문일 불여일견이라고 코드를 한번 봅시다.
using System;
using System.Linq;
namespace StudyCSharp
{
class MainApp
{
static void Main(string[] args)
{
Profile[] profile_list =
{
new Profile() { name = "정우성", age = 13 },
new Profile() { name = "김태희", age = 23 },
new Profile() { name = "창모", age = 26 },
new Profile() { name = "김하온", age = 20},
new Profile() { name = "이병재", age = 24},
new Profile() { name = "이순재", age = 70}
};
var profiles = from profile in profile_list
where profile.age < 30
orderby profile.name
select profile;
foreach(Profile profile in profiles)
{
Console.WriteLine(profile.name + profile.age.ToString());
}
}
}
class Profile //사람에대한클래스
{
public string name //사람의 이름
{
get; set;
}
public int age //사람의 나이
{
get; set;
}
}
}
위코드는 사람에대한 class profile을 만들고 그의 객체를 생성하여 profile_list 배열에 담았습니다.
이후 profile_list에서는 아래와 같은 코드로 몇개의 객체를 생성하고 있습니다.
var profiles = from profile in profile_list
where profile.age < 30
orderby profile.name
select profile;
위 구문은 아래와 같이 해석합니다.
1. from profile in profile_list : profile_list 배열에서 원소들을 profile에 할당한다.
2. where profile.age < 30 : 그 원소들중에서 age속성이 30이하인 값만 선택한다.
3. orderby profile.name : 위에서 선택한 원소들을 profile.name 오름차순 ㄱ ->ㅎ 순으로 정렬한다.
4. select profile : 이후 profile객체를 선택하여 var profiles에 넣는다.
위와 같은 방법으로 손쉽게 데이터를 골라낼수 있습니다.
그렇다면 골라낸 var profiles의 형식은 무엇일까요? LINQ의 질의 결과는 IEnumerable<T>로 반환됩니다.
T는 select문에서 결정됩니다.
만약 profile이 select 되었다면, IEnumerable<profile>이 반환됩니다.
만약 profile.name이 select 되었다면, IEnumerable<string>이 반한될 것 입니다.
<여러개의 데이터원본에 질의하기>
만약, 데이터를 모아놓은 배열안에 또다른 배열이 있다면 어떻게 골라내는지 보겠습니다.
코드를 보시죠.
using System;
using System.Linq;
namespace StudyCSharp
{
class MainApp
{
static void Main(string[] args)
{
Student[] student_list =
{
new Student() { name = "정우성", score = new int[]{11, 23, 33, 12 } },
new Student() { name = "김태희", score = new int[]{23, 25, 33, 12 } },
new Student() { name = "창모", score = new int[]{22, 12, 42, 21 } },
new Student() { name = "김하온", score = new int[]{24, 11, 24, 24 } },
new Student() { name = "이병재", score = new int[]{53, 12, 22, 31 } },
new Student() { name = "이순재", score = new int[]{44, 52, 12, 12 }}
};
var students = from student in student_list //student_list에서 객체를 student에 할당
from score in student.score //할당된 student객체의 score배열의 원소를 score에 할당
where score >30 //score이 30보다 큰 것만 걸러내고
select new {student.name , score}; //student.name이랑 score만 저장
foreach(var student in students)
{
Console.WriteLine(student.name + student.score);
}
}
}
class Student //사람에대한클래스
{
public string name //사람의 이름
{
get; set;
}
public int[] score;//학생의 점수들
}
}
student 객체안에는 score이란 점수 배열이 있습니다.
또한 student_list 배열안에는 student 객체들이 존재합니다.
이처럼 2개의 배열이 있을때 from in 구문을 2번 이용하여 객체들을 골라낼 수 있습니다.
위의 결과는 아래와 같습니다.
<group by로 데이터 분류하기>
where문으로 데이터를 분류할 수 도 있지만, group by로 데이터를 분류할 수 도 있습니다.
하지만 쓰임이 조금은 다른데요, 먼저 코드를 봅시다.
using System;
using System.Linq;
namespace StudyCSharp
{
class MainApp
{
static void Main(string[] args)
{
Student[] student_list =
{
new Student() { name = "정우성", score = 23 },
new Student() { name = "김태희", score = 25, },
new Student() { name = "창모", score = 42 },
new Student() { name = "김하온", score = 11},
new Student() { name = "이병재", score = 22},
new Student() { name = "이순재", score = 22 }
};
var students = from student in student_list
group student by student.score >= 25 into g
select new { group_key = g.Key, student = g};
foreach(var student_group in students) //Key값이 true인 배열과 false인 배열을 student_group에 담는다
{
Console.WriteLine($"점수가 25점 이상인가요?{student_group.group_key}");
foreach(var student in student_group.student)
{
Console.WriteLine($"{student.name} : {student.score}");
}
}
}
}
class Student //사람에대한클래스
{
public string name //사람의 이름
{
get; set;
}
public int score;//학생의 점수들
}
}
위코드는 학생에대한 클래스를 만들고 점수와 이름을 담는 속성을 만듭니다.
이후 학생들을 여러 객체를 만들어 student_list에 담아놉니다.
이후 student_list에서 데이터를 뽑으려고 합니다.
아래는 LINQ구문입니다.
var students = from student in student_list
group student by student.score >= 25 into g
select new { group_key = g.Key, student = g};
1. from student in student_list : student_list에서 배열의 원소들 즉 학생 객체들을 student에 담습니다.
2. group student by student.score >= 25 into g : student객체를 student.score이 25보다 큰 학생이랑 아닌학생을 그룹지어서 g 그룹에 저장합니다. 이때, g에는 g.Key값이 존재합니다. score이 25보다 크면 true가 저장되고 작으면 false가 저장됩니다. 떠힌 그 Key값에 맞는 객체들을 배열안에 넣어서 그 배열을 g에 할당합니다.
특히 foreach문을 사용할때, 원래는 안의 원소들이 하나씩 할당되지만 group 객체일경우 Key값에 따라서 true값과 false 값이 하나씩 할당됩니다.
따라서 위의 코드에서 foreach문이 2개인걸 알 수 있습니다.
위 코드의 실행결과는 아래와 같습니다.
<내부 join>
두 원본 데이터를 합칠때 join문을 사용하면 편리합니다.
예를 들어 아래와 같은 데이터들이 있을때,
{name = "", score = }, {name = "", height = }
name이라는 공통된 요소를 가지고 score과 height을 하나의 데이터로 만들 수 있다는 뜻입니다.
코드를 보시죠.
using System;
using System.Linq;
namespace StudyCSharp
{
class MainApp
{
static void Main(string[] args)
{
Student_Score[] student_scores =
{
new Student_Score(){name = "창모", score = 12},
new Student_Score(){name = "제키와이", score = 21},
new Student_Score(){name = "페이커", score = 55},
new Student_Score(){name = "장기하", score = 42},
new Student_Score(){name = "아이유", score = 33},
new Student_Score(){name = "더콰이엇", score = 44}
};
Student_Height[] student_heights =
{
new Student_Height(){name = "창모", height = 188 },
new Student_Height(){name = "뷔", height = 177 },
new Student_Height(){name = "정우성", height = 189 },
new Student_Height(){name = "현아", height = 163 },
new Student_Height(){name = "페이커", height = 177 },
new Student_Height(){name = "아이유", height = 162 },
};
var students = from student_score in student_scores
join student_height in student_heights on student_score.name equals student_height.name
select new
{
name = student_score.name,
score = student_score.score,
height = student_height.height
};
foreach(var student in students)
{
Console.WriteLine($"이름 : {student.name}, 점수 : {student.score}, 키 : {student.height}");
}
}
}
class Student_Score
{
public string name //학생의 이름
{
get; set;
}
public int score;//학생의 점수들
}
class Student_Height
{
public string name //학생의 이름
{
get; set;
}
public int height; //학생의 키
}
}
이름과 점수가 있는 Student_Score 객체를 모아놓은 student_scores[] 배열과 이름과 키가 있는 Student_Height 객체를 모아놓은 student_height[] 배열이 있습니다.
이 두개의 데이터를 LINQ하는 구문을보면 아래와 같습니다.
var students = from student_score in student_scores
join student_height in student_heights on student_score.name equals student_height.name
select new
{
name = student_score.name,
score = student_score.score,
height = student_height.height
};
join student_height in student_heights on student_score.name equals student_heught 구문은 아래와 같이 해석합니다.
student_heights 배열에서 객체들을 하나씩 student_height에 담고 student_score.name과 studnet_height.name을 비교해서, 이름이 같은 student_score객체만 남기고 버립니다.
주요한점은 처음 선택한 객체를 선택한다는 것입니다.
위코드는 아래의 결과를 갖습니다.
<외부 join>
앞에서 배운 join은 내부 조인으로 equal값이 맞는 값만 select 됩니다.
이를 내부join이라고 합니다.
또한, equal값이 맞는 값과 맞지않는 값 둘다 selct 하는것을 외부 join이라고합니다.
외부 join은 group 구문에서 사용햇던 into 연산자를 사용해서 구현합니다.
또한 equal값이 아닌 속성을 디폴트 값으로 초기화하는 메소드는 DefultIfEmpty()입니다.
먼저 코드부터 보시죠.
using System;
using System.Linq;
namespace StudyCSharp
{
class MainApp
{
static void Main(string[] args)
{
Student_Score[] student_scores =
{
new Student_Score(){name = "창모", score = 12},
new Student_Score(){name = "제키와이", score = 21},
new Student_Score(){name = "페이커", score = 55},
new Student_Score(){name = "장기하", score = 42},
new Student_Score(){name = "아이유", score = 33},
new Student_Score(){name = "더콰이엇", score = 44}
};
Student_Height[] student_heights =
{
new Student_Height(){name = "창모", height = 188 },
new Student_Height(){name = "뷔", height = 177 },
new Student_Height(){name = "정우성", height = 189 },
new Student_Height(){name = "현아", height = 163 },
new Student_Height(){name = "페이커", height = 177 },
new Student_Height(){name = "아이유", height = 162 },
};
var students = from student_score in student_scores
join student_height in student_heights on student_score.name equals student_height.name into ps
from student_height in ps.DefaultIfEmpty(new Student_Height() { height = 0 })
select new
{
name = student_score.name,
score = student_score.score,
height = student_height.height
};
foreach(var student in students)
{
Console.WriteLine($"이름 : {student.name}, 점수 : {student.score}, 키 : {student.height}");
}
}
}
class Student_Score
{
public string name //학생의 이름
{
get; set;
}
public int score;//학생의 점수들
}
class Student_Height
{
public string name //학생의 이름
{
get; set;
}
public int height; //학생의 키
}
}
위 코드의 실행결과는 아래와 같습니다.
여기서 사용되는 데이터의 키나 점수는 임의로 넣은것이니 오해없길 바랍니다.
'Programming > C#' 카테고리의 다른 글
c# 언어 공부 - 12(애트리뷰트) (0) | 2022.05.13 |
---|---|
c# 언어 공부 - 11(리플렉션) (0) | 2022.05.12 |
c# 언어 공부 - 9(대리자와 이벤트) (0) | 2022.05.10 |
c# 언어 공부 - 8(예외 처리) (0) | 2022.05.07 |
c# 언어 공부 - 7(일반화 프로그래밍) (0) | 2022.05.06 |