LINQ
- Language Intergrated Query
- 데이터를 질의하고 조작하기 위해 일관되고, 직관된 문법
LINQ 이점
- 가독성과 표현력
- 타입 안정성 및 컴파일 검사
- 코드 재사용성
- C#과 통합
LINQ 기본형
_items = from item in items
where item.code > 1
orderby item.code
select item;
_items = items
.Where(x => x.code > 1)
.OrderBy(x => x.code);
LINQ 식
- Query 구문을 작성할 때 사용되는 LINQ 식
from
IEnumerable<Item> _items = from item in items
select item;
foreach (var _item in _items) _item.Print();
중복 from
- 열거형 내 열거형이 중첩된 경우 from을 통한 접근 가능
- select new { } 를 통한 익명 형식 생성
- school.cs
class School{
public string name;
public int[] scores;
}
School[] schools =
{
new School(){ name = "A반", scores = new int[] { 70, 60, 50, 40 },
new School(){ name = "B반", scores = new int[] { 10, 20, 30, 40 }
};
* enumerable
```cs
var _schools = from classes in schools
from scores in classes.scores
where scores < 50
select new { name = classes.name, lowScore = scores };
select
- 데이터에서 어떤 항목을 추출할 것인지
- item이 아닌 item.code(int) 값을 추출
- IEnumerable 제네릭 타입도 int가 되어야 함
- select new { } 를 통한 익명 형식 생성
- 원하는 구조체를 반환하듯 new { }를 통해 원하는 형식 선택
IEnumerable<int> codes = from item in items
select item.code;
foreach (var code in codes)
print(code);
where
_items = from item in items
where item.code > 1
select item;
orderby
_items = from item in items
orderby item.name
select item;
_items = from item in items
orderby item.name ascending
select item;
_items = from item in items
orderby item.name descending
select item;
IEnumerable <=> Array/List 변환
- (IEnumerable 타입).ToArray()
- (IEnumerable 타입).ToList()
int[] codes = (from item in items
select item.code).ToArray();
List<int> codes2 = (from item in items
select item.code).ToList();
group ... by
var _itemGroups = from item in items
group item by item.code > 2 into g
select new { GroupKey = g.Key, GroupItem = g };
foreach (var _itemGroup in _itemGroups)
{
print($"2보다 큼? : {_itemGroup.GroupKey}");
foreach (Item item in _itemGroup.GroupItem)
item.Print();
}
내부 조인 : join on
- from을 기준으로 존재하는 부분만 합치기
- UserDB
class User
{
public int userCode;
public string userName;
}
User[] users =
{
new User() { userCode = 2, userName = "A"},
new User() { userCode = 1, userName = "B"},
new User() { userCode = 3, userName = "C"},
new User() { userCode = 5, userName = "D"}
};
User[] users2 =
{
new User() { userCode = 2, userName = "A"},
new User() { userCode = 2, userName = "A+"},
new User() { userCode = 1, userName = "B"},
new User() { userCode = 3, userName = "C"},
new User() { userCode = 3, userName = "C+"},
new User() { userCode = 5, userName = "D"}
};
var itemJoins =
from item in items
join user in users
on item.code equals user.userCode
select new {Code = item.code, UserNmae = user.userName, ItemName = item.name};
외부 조인 : join on
- from을 기준으로 모든 부분을 합치기
- DefaultIfEmpty : 빈 값은 기본 값으로 채운다.
- 외부조인
var itemJoins =
from item in items
join user in users
on item.code equals user.userCode into excepts
from user in excepts.DefualtIfEmpty(new User() { userName = "NONE" })
select new { Code = item.code, UserName = user.userName, ItemName = item.name };
foreach(var itemjoin in itemjoins) print(itemjoin);
LINQ 함수
- Method 구문을 작성할 때 사용되는 LINQ 함수
- Array, List 등의 반복 가능한 IEnumerable에 적용
- 반환 값도 IENumerable<T>
IEnumerable<Item> _items;
Item[] items = new Item[]
{
new Item(2, "carrot"),
new Item(1, "apple"),
new Item(3, "banana"),
new Item(4, "orange"),
};
정렬 : Order By
_items = items.OrderBy(x => x.code);
_ites = items.OrderByDescending(x => x.code);
정렬 : ThenBy
_items = items.OrderBy(x => x.code).ThenBy(x => x.name);
_items = items.OrderBy(x => x.code).ThenByDescending(x => x.name);
반전 : Reverse
- IEnumerable를 뒤집는다.(반전시킨다.)
_items = items.Reverse();
집합 연산
Distince
- 중복 제거, Item : System.IEquatable<Item> 인터페이스 구현
_items = items4.Distinct();
Except
_items = items.Except(items2);
Intersect
_items = items.Intersect(items2);
Union
_items = items.Union(items2);
조건
Where
- 조건이 true인 값을 선택
- 다른 조건 함수와 다르게 IEnumerable<T> 타입 리턴
_items = items.Where(x => x.code > 1);
All
bool b = items.All(x => x.code > 0);
print(b);
Any
bool b = items.Any(x => x.code > 3);
print(b);
Contains
bool b = items.Contains(new Item(3, "banana"), new ItemComparer());
print(b);
데이터 추출
Select
- 값을 추출해 IEnumerable<T> 타입 리턴
- Array/List 등의 타입을 IEnumerable 타입으로 변환
- new {}를 통한 익명 형식 적용 가능
IEnumerable<int> codes = items.Select(x => x.code);
foreach (int code in codes)
print(code);
var _customItems = items.Select(x => new { Name = x.name, CodeAdd = x.code + 1 });
foreach (var _customItem in _customItems)
print(_customItem);
SelectMany
int[] number = new int[] { 10, 20 };
string[] animals = new string[] { "cat", "dog", "donkey" };
var mix = number.SelectMany(num => animals, (n, a) => new { n, a });
foreach (var a in mix)
print(a);
데이터 분할
Skip
_items = items.Skip(2);
SkipWhile
- 순서대로 정렬됐을 때, true일 동안 스킵
- {1, 2, 3, 4}에서 3보다 작을 동안을 스킵, 3, 4만 나옴
int[] number = new int[] { 10, 20 };
string[] animals = new string[] { "cat", "dog", "donkey" };
var mix = number.SelectMany(num => animals, (n, a) => new { n, a });
foreach (var a in mix)
print(a);
Take
_items = items.Take(2);
TakeWhile
- 순서대로 정렬됐을 때, true일 동안 가져오기
- {1, 2, 3, 4}에서 3보다 작을 동안 가져오기, 1, 2만 나옴
_items = items.OrderBy(x => x.code).TakeWhile(x => x.code < 3);
데이터 결합
Join(내부 조인)
- 존재하는 부분만 합하기, 외부join은 LINQ식을 참고
var itemJoins = items.Join(users, item => item.code, user => user.userCode,
(item, user) => new { Code = item.code, UserName = user.userName, ItemName = item.name });
foreach (var itemJoin in itemJoins)
print(itemJoin);
GroupJoin
var itemGroupJoins = items.GroupJoin(users2, item => item.code, user2 => user2.userCode,
(item, users2Collection) => new {
Code = item.code,
ItemName = item.name,
UserNames = users2Collection.Select(user2 => user2.userName)
});
foreach (var itemGroupJoin in itemGroupJoins)
{
print($"Code : {itemGroupJoin.Code}, ItemName : {itemGroupJoin.ItemName}");
foreach (var _UserName in itemGroupJoin.UserNames)
print(_UserName);
}
데이터 그룹화
GroupBy
var itemGroups = items.GroupBy(item => item.code > 2, item => item,
(key, item) => new { Key = key, Item = item });
foreach (var itemGroup in itemGroups)
{
print($"2보다 큼? : {itemGroup.Key}");
foreach (var item in itemGroup.Item)
item.Print();
}
ToLookUp
- 데이터를 그룹으로 묶어준다.
- 키를 자동으로 생성
var itemGroups = items.ToLookup(item => item.code > 2, item => item);
foreach(var itemGroup in itemGroups)
{
print($"2보다 큼? : {itemGroup.Key}");
foreach(var item in itemGroup)
item.Print();
}
생성
DefaultIfEmpty
List<Item> itemEmpty = new List<Item>();
Item defaultItem = new Item(-1, "NONE");
foreach (Item item in itemEmpty.DefaultIfEmpty(defaultItem)) item.Print();
Empty
IEnumerable<Item> emptyItem = Eunmerable.Empty<Item>();
print(emptyItem.Count());
Range
- 정수 범위의 숫자들을 생성, 시작 숫자, 개수 명시
IEnumerable<int> ranges = Enumerable.Range(3, 10).Select(x => x*x);
foreach (var range in ranges) print(range);
Repeat
- 특정 데이터를 반복하는 IEnumerable 생성
IEnumerable<string> strings = Enumerable>Repeat("Hi", 10);
foreach (var str in strings) print(str);
동등 평가
SequenceEqual
bool b = items.SequenceEqual(items3);
print(b);
요소 접근
ElementAt
Item _item = items.ElementAt(3);
_item.Print();
ElementAtOrDefault
- 특정 인덱스의 원소를 가져옴
- 특정 인덱스의 원소가 null일 경우 타입의 기본 값 리턴
Item _item = items.ElementAt(3);
_item.Print();
First
Item _item = items.First();
_item = items.First(x => x.code > 2);
_item.Print();
FirstOrDefault
- 첫번째 원소를 가져옴
- 첫번째 원소가 null일 경우 기본 값 리턴
Item _item = items.FirstOrDefault();
_item = items.FirstOrDefault(x => x.code > 1);
_item.Print();
Last
Item _item = items.Last();
_item = items.Last(x => x.code > 2);
_item.Print();
LastOrDefault
- 마지막 인덱스의 원소를 가져옴
- 마지막 원소가 null일 경우 기본 값 리턴
Item _item = items.LastOrDefault();
_item = items.LastOrDefault(x => x.code < 4);
_item.Print();
Single
- IEnumerable에 조건을 만족하는 단 하나의 원소 리턴
- 단 하나만 이 조건을 만족시켜야 InvalidOperationException에러가 안 뜸
Item _item = items.Single(x => x.code > 3);
_item.Print();
SingleOrDefault
- IEnumerable에 조건을 만족하는 단 하나의 원소 리턴
- 해당 원소가 null일 경우 기본 값 리턴
Item _item = items.SingleOrDefault(x => x.code > 3);
_item.Print();
형식 변환
AsEnumerable
IEnumerable<Item> _items = items.AsEnumerable();
AsQueryable
- IQueryable 형식(IQueryable ⊃ IEnumerable)으로 변환
- LINQ를 SQL과 연동하는데 사용
IQueryable<Item> _items = items.AsQueryable();
Cast
- ArrayList에서 특정 타입 원소만 추출
- 실패 시, InvaildCastException 에러
ArrayList itemArray = new ArrayList { 1, 2, 3, 4, 5 };
IEnumerable<int> _items = itemArray.Cast<int>();
foreach (var _item in _items) print(_item);
OfType
- ArrayList에서 특정 타입 원소만 추출
- 실패는 건너뛰고 2와 4가 나옴
ArrayList itemArray = new ArrayList { 1, "2", 3, "4", 5 };
IEnumerable<string> _items = itemArray.OfType<string>();
foreach (string _item in _items) print(_item);
ToArray
List<string> strs = new List<string>() { "zz", "ss" };
string[] _strs = strs.ToArray();
ToList
List<Item> itemList = items.ToList();
ToDictionary
Dictionary<int, Item> itemDict = items.ToDictionary(x => x.code);
foreach(var keyValue in itemDic)
{
print($"키 : {keyValue.Key}");
keyValue.Value.Print();
}
ToLookup
- 키와 값 형식을 가진 Lookup 형태로 만듬
ILookup<int, sring> lookup = items.ToLookup(x => x.code, y => y.name);
foreach(var grouop in lookup)
{
print($"키 : {group.Key}");
foreach(var g in group) print($"값 : {g}");
}
연결
Concat
IEnumerable<string> strs = items.Select(x => x.name).Concat(items2.Select(x => x.name));
foreach(var str in strs) print(str);
집계
Aggregate
int total = items.Aggregate(0, (total, next) => total += next.code);
print(total);
Average
double avg = items.Agerage(x => x.code);
print(avg);
Count
int count = items.Count();
print(count);
LongCount
long longCount = items.LongCount();
print(longCount);
Max
int maxCode = items.Max(x => x.code);
print(maxCode);
Min
int minCode = items.Min(x => x.code);
print(minCode);
Sum
int sum = items.Sum(x => x.code);
print(sum);
코드
public class Item : System.IEquatable<Item>
{
public int code;
public string name;
public Item(int code, string name)
{
this.code = code;
this.name = name;
}
public void Print()
{
Debug.Log($"code : {code}, name : {name}");
}
public bool Equals(Item other)
{
if (ReferenceEquals(other, null)) return false;
if (ReferenceEquals(this, other)) return true;
return code.Equals(other.code) && name.Equals(other.name);
}
public override int GetHashCode()
{
int hashItemName = name == null ? 0 : name.GetHashCode();
int hashItemCode = code.GetHashCode();
return hashItemName ^ hashItemCode;
}
}
class ItemComparer : IEqualityComparer<Item>
{
public bool Equals(Item x, Item y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
return false;
return x.code == y.code && x.name == y.name;
}
public int GetHashCode(Item item)
{
if (ReferenceEquals(item, null)) return 0;
int hashProductName = item.name == null ? 0 : item.name.GetHashCode();
int hashProductCode = item.code.GetHashCode();
return hashProductName ^ hashProductCode;
}
}