[Unity/C#]LINQ

강동현·2024년 2월 19일
0

Unity/C#

목록 보기
20/26

LINQ

  • Language Intergrated Query
  • 데이터질의하고 조작하기 위해 일관되고, 직관된 문법

LINQ 이점

  • 가독성과 표현력
  • 타입 안정성 및 컴파일 검사
  • 코드 재사용성
  • C#과 통합

LINQ 기본형

  • 1. Query 구문 : LINQ 식
    • DB SQL과 같은 선언방식
_items = from item in items
         where item.code > 1
         orderby item.code
         select item;
  • 2. Method 구문 : LINQ 함수
    • 함수식 Query 사용
_items = items
.Where(x => x.code > 1)
.OrderBy(x => x.code);

LINQ 식

  • Query 구문을 작성할 때 사용되는 LINQ 식

from

  • 어떤 데이터에서 원하는 값을 추출할 것인지
    • items 배열에서 각 item 추출
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

  • 데이터 정렬(default = 오름차순)
//default : 오름차순
_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 타입을 배열로 변환
  • (IEnumerable 타입).ToList()
    • IEnumerable 타입을 리스트로 변환
int[] codes = (from item in items
               select item.code).ToArray();
List<int> codes2 = (from item in items
                    select item.code).ToList();

group ... by

  • 데이터를 그룹에 따라 분류할 수 있다.
// group : 데이터 분류하기
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

//code 오름차순
_items = items.OrderBy(x => x.code);
//code 내림차순
_ites = items.OrderByDescending(x => x.code);

정렬 : ThenBy

  • 정렬 수행 후, 다음 정렬 기준 설정에 사용
//1. code 오름차순 / 2. name 오름차순
_items = items.OrderBy(x => x.code).ThenBy(x => x.name);
//1. code 오름차순 / 2. 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 - items2
_items = items.Except(items2);

Intersect

  • 교집합 items ∩ items2
_items = items.Intersect(items2);

Union

  • 합집합 items ∪ items2
_items = items.Union(items2);

조건

Where

  • 조건이 true인 값을 선택
  • 다른 조건 함수와 다르게 IEnumerable<T> 타입 리턴
// Where : 조건이 true인 값을 선택
_items = items.Where(x => x.code > 1);

All

  • 모두 true -> true 반환
bool b = items.All(x => x.code > 0);
print(b);

Any

  • 하나라도 true -> true 반환
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

  • 두 IEnumerable의 모든 조합을 만듬
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

  • group + join 그룹을 지으면서 합함
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를 생성
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

  • 두 IEnumerable이 동일한지 평가
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 형식으로 변환
IEnumerable<Item> _items = items.AsEnumerable();

AsQueryable

  • IQueryable 형식(IQueryable ⊃ IEnumerable)으로 변환
  • LINQSQL연동하는데 사용
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을 하나로 연결
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 형식 개수
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);
코드
  • Item 클래스
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;
    }
}
  • ItemComparer 코드
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;
    }
}
profile
GAME DESIGN & CLIENT PROGRAMMING

0개의 댓글