C# Where, Select, OrderBy, List.Find(All)를 정리해보려 합니다.
이 셋의 공통점을 정리해 보겠습니다.
1. 이 메서드들은 IEnumarable 인터페이스에서 제공하고 있습니다 (Find는 리스트).
이 말은 IEnumerable을 상속하고 있는 모든 클래스에서 사용할 수 있음을 뜻합니다.
대표적으로 System.Collections에 있는 데이터 묶음 클래스가 되겠습니다.
List, ArrayList, Dictionary, Stack, Queue등 거의 모든 Collections에서 사용 가능합니다.
2. 이 메서드들은 Func를 매개변수로 받을 수 있습니다.
이 말은 함수로 그때그때 필요한 값을 찾아올 수 있음을 뜻합니다.
예를들어 해당 값보다 작거나, 크거나 같거나 등등 그런 값들만을
제가 지정한대로 가져올 수 있습니다.
이러한 이유로 3가지 대표적인 함수를 가져왔으며 이를 응용할 수 있다면
Collections를 다루는 스킬이 늘 것을 장담합니다.
Where 메서드.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Enumerable
{
class Program
{
class Item
{
public int id;
public string name;
public int damage;
public Item(int id, string name, int damage)
{
this.id = id;
this.name = name;
this.damage = damage;
}
}
static void Main(string[] args)
{
//테스트를 위한 데이터 생성.
Item[] itemArr = {
new Item(0, "장검", 10),
new Item(1, "단검", 8),
new Item(2, "활", 12),
new Item(3, "총", 15),
new Item(4, "도끼", 20)
};
//테스트를 위한 데이터 삽입.
var itemDIct = new Dictionary<int, Item>();
var itemList = new List<Item>();
foreach (var item in itemArr)
{
}
//where 사용.
//itemDict에서 데미지가 12미만인 아이템 가져오기.
foreach (var item in items1)
Console.WriteLine("key : {0}, 아이템 이름 : {1}, 데미지 : {2}",
//items1의 타입 : IEnumerable<KeyValuePair>Select
//실행 결과
//key : 0, 아이템 이름 : 장검, 데미지 : 10
//key : 1, 아이템 이름 : 단검, 데미지 : 8
//itemList에서 데미지가 12 미만인 아이템 가져오기.
foreach (var item in items2)
Console.WriteLine("id : {0}, 아이템 이름 : {1}, 데미지 : {2}",
//items2의 타입 : IEnumerable<Item>
//실행 결과
//id : 0, 아이템 이름 : 장검, 데미지 : 10
//id : 1, 아이템 이름 : 단검, 데미지 : 8
}
}
}
|
우선 기본적인 Item 클래스를 정의했습니다. id, 이름, 데미지를 가지고 있습니다.
인스턴스들을 생성해 배열에 넣어두었습니다.
Where은 조건을 집어넣어 조건에 맞는 인스턴스 또는 값들을 가져옵니다.
첫번째 Dictionary에서의 예시 입니다.
key값으로 아이템의 id를 사용했으며 value로 item객체 그 자체를 넣었습니다.
Where 사용시 x => x.Value.damage가 있습니다. 딕셔너리의 반복을 돌때 가져오는 타입 자체가
KeyValuePair 이기 때문에 Value의 데미지가 작은 것을 가져오도록 했습니다.
x에 KeyValuePair<int, item>을 하나씩 가져오며 x의 조건이 맞는지 검사해 맞다면 인스턴스를 선택합니다.
마지막으로 선택된 모든 인스턴스를 IEnumerable<KeyValuePair<int, Item>>타입으로 반환합니다.
Dictionary 사용시 페어로 가져오는 것이 불편하다면
itemDict.Values.Where(x => x.damage < 12) 이런 식으로 Value만 뽑아 사용하시면 되겠습니다.
또는 itemDict.Values.ToList()를 사용해 List타입으로 Values만 뽑아 사용하셔도 무방합니다.
두번째 List에서의 예시 입니다.
List의 경우는 훨씬 쉽습니다. x에 Item 인스턴스를 하나씩 대입해보며 조건이 맞다면 선택합니다.
마지막으로 선택된 모든 Item들을 IEnumerable<Item> 타입으로 반환합니다.
Select 메서드.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Enumerable
{
class Program
{
class Item
{
public int id;
public string name;
public int damage;
public Item(int id, string name, int damage)
{
this.id = id;
this.name = name;
this.damage = damage;
}
}
static void Main(string[] args)
{
//테스트를 위한 데이터 생성.
Item[] itemArr = {
new Item(0, "장검", 10),
new Item(1, "단검", 8),
new Item(2, "활", 12),
new Item(3, "총", 15),
new Item(4, "도끼", 20)
};
//테스트를 위한 데이터 삽입.
var itemDIct = new Dictionary<int, Item>();
var itemList = new List<Item>();
foreach (var item in itemArr)
{
}
//Select 사용.
//itemDict에서 데미지만을 가져옴.
foreach (var damage in damages)
Console.WriteLine("데미지 : {0}", damage);
//damages 타입 : IEnumerable<int>
//출력 결과.
//데미지 : 10
//데미지 : 8
//데미지 : 12
//데미지 : 15
//데미지 : 20
//itemDict에서 데미지가 12보다 큰지 검사.
foreach (var isBigger in isBiggerDamages)
Console.WriteLine("데미지가 12보다 큰가 : {0}", isBigger);
//biggerDamages 타입 : IEnumerable<bool>
//출력 결과.
//데미지가 12보다 큰가 : false
//데미지가 12보다 큰가 : false
//데미지가 12보다 큰가 : false
//데미지가 12보다 큰가 : true
//데미지가 12보다 큰가 : true
var totalDamages = itemDIct.Select(x => x.Value.damage + 20);
foreach (var totalDamage in totalDamages) Console.WriteLine("계산된 총 데미지 : {0}", totalDamage); //totalDamages 타입 : IEnumerable //출력 결과. //계산된 총 데미지 : 30 //계산된 총 데미지 : 28 //계산된 총 데미지 : 32 //계산된 총 데미지 : 35 //계산된 총 데미지 : 40 }
}
}
|
Item 클래스와 데이터 세팅은 위와 같습니다.
Select는 Where과 달리 조건에 맞는 인스턴스가 아닌
그 값 자체를 가져옵니다. 자세한 설명은 예시를 통해 하겠습니다.
첫번째 Dictionary 예시.
var damages = itemDict.Selct(x => x.Value.damage); 라인이 있습니다.
아이템 딕셔너리에서 x에 KeyValuePair를 하나씩 대입하고 그 Value의 데미지를 가져오도록 했습니다.
조건에 맞는 인스턴스가 아닌 damage그 자체를 가져오도록 합니다.
그 결과로 damages의 결과는 IEnumerable<int>가 됩니다.
두번째 Dictionary예시.
var isBiggerDamages = itemDict.Select(x => x.Value.damage > 12); 라인이 있습니다.
isBiggerDamages의 반환된 타입은 IEnumerable<bool>타입이 됩니다!!!!
이것이 아주 중요한데 x에 KeyValuePair<int, Item>타입 인스턴스를 하나씩 넣어볼겁니다.
다음 Value의 데미지를 확인하고 조건의 결과 자체를 반환 한다는 겁니다.
세번째 Dictionary예시.
예를들어 이런 경우가 있을 겁니다. 캐릭터의 힘이 20이고 무기 데미지에 힘을 더해
총 데미지를 반환하고 싶습니다! (나만 그런가....)
그런 경우 다음과 같이 사용할 수 있습니다.
var totalDamages = itemDict.Select(x => x.Value.damage + 20);
그럼 totalDamages의 타입은 IEnumerable<int>타입이 되며 데미지에 20을 더한 값이 들어옵니다.
x.Value.damage + 20의 값 자체가 반환되는 것입니다.
Find, FindAll 예시.
Find, FindAll 메서드는 List에서 제공합니다. 내용이 비슷하고 유용해 가지고 왔습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Enumerable
{
class Program
{
class Item
{
public int id;
public string name;
public int damage;
public Item(int id, string name, int damage)
{
this.id = id;
this.name = name;
this.damage = damage;
}
}
static void Main(string[] args)
{
//테스트를 위한 데이터 생성.
Item[] itemArr = {
new Item(0, "장검", 10),
new Item(1, "단검", 8),
new Item(2, "활", 12),
new Item(3, "총", 15),
new Item(4, "도끼", 20)
};
//테스트를 위한 데이터 삽입.
var itemDIct = new Dictionary<int, Item>();
var itemList = new List<Item>();
foreach (var item in itemArr)
{
}
//FInd, FindAll 사용.
//id가 3인 아이템 찾기.
//targetItem 타입 : Item
//출력 결과.
//id : 3, 아이템 이름 : 총.
var targetList = itemList.FindAll(x => x.damage > 12);
foreach (var target in targetList)
//targetList 타입 : List<item>
//출력 결과.
//이름 : 총, 데미지 : 15
//이름 : 도끼, 데미지 : 20
}
}
}
|
Find, FindAll 함수는 너무나 간단합니다.
Find메서드 사용,
targetItem = itemList.Find(x => x.id == 3); 라인이 있습니다.
x에 Item 객체를 하나씩 넣으며 id가 3이라면 반환합니다.
주의할 점은 id가 3인 것이 없다면 기본값(여기선 null) 을 반환하며
id == 3인 것이 여러개라면 가장 처음 발견된 것을 반환합니다.
FindAll메서드 사용.
var targetList = itemList.FindAll(x => x.damage > 12); 라인이 있습니다.
데미지가 12 이상인 모든 객체를 반환합니다.
Where과의 차이점.
FindAll을 사용할 시 위의 예시에서 반환 타입은 List<item>이 됩니다.
Where을 사용시 IEnumerable<Item>이 됩니다. 기능은 완전히 유사합니다.
itemList.Where(x => x.damage > 12).ToList()를 할 시에 List<Item>타입으로 반환되며
FindAll을 사용한 것과 완전히 같습니다.
OrderBy, OrderByDescending사용,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Enumerable
{
class Program
{
class Item
{
public int id;
public string name;
public int damage;
public Item(int id, string name, int damage)
{
this.id = id;
this.name = name;
this.damage = damage;
}
}
static void Main(string[] args)
{
//테스트를 위한 데이터 생성.
Item[] itemArr = {
new Item(0, "장검", 10),
new Item(1, "단검", 8),
new Item(2, "활", 12),
new Item(3, "총", 15),
new Item(4, "도끼", 20)
};
//테스트를 위한 데이터 삽입.
var itemDIct = new Dictionary<int, Item>();
var itemList = new List<Item>();
foreach (var item in itemArr)
{
}
//OrderBy사용.
//List에서 사용.
var orderedList = itemList.OrderBy(x => x.damage);
foreach (var item in orderedList)
//출력 결과.
//이름 : 단검, 데미지 : 8
//이름 : 장검, 데미지 : 10
//이름 : 활, 데미지 : 12
//이름 : 총, 데미지 : 15
//이름 : 도끼, 데미지 : 20
//Dictionary에서 사영.
var orderedDict = itemDIct.OrderByDescending(x => x.Key);
foreach (var item in orderedDict)
//출력 결과.
//id : 4, 이름 : 도끼.
//id : 3, 이름 : 총.
//id : 2, 이름 : 활.
//id : 1, 이름 : 단검.
//id : 0, 이름 : 장검.
}
}
}
|
OrderBy 사용 입니다. 매개변수로 넣은 멤버를 기준으로 값을 정렬합니다.
첫번째 리스트 에서의 예시.
var orderedList = itemList.OrderBy(x => x.damage); 라인이 있습니다.
직관적으로 orderedList에 정렬된 리스트가 들어가 있음을 알 수 있습니다.
주의할 점은 반환 타입이 IOrderedEnumerable<Item>타입 이라는 겁니다.
List<Item>타입이 아니지만 Enumerable 인스턴스 이기 때문에 사용법은 Where, Select
에서 반환받은 것과 같게 사용하시면 되겠습니다.
추가 정보로 List에서는 BinarySearch 함수를 제공합니다.
바이너리 서치는 아주 빠른 시간에 리스트를 검색하는 기능이지만 반드시 정렬되어 있어야 합니다.
OrderBy를 사용하고 바이너리 서치를 이용하시면 되겠습니다.
사용시 주의할 점은 Sort 메서드와 달리 반환 값을 가집니다! 정렬된 값을 저장하고 사용해야 합니다.
두번째 딕셔너리 에서의 예시.
var orderedDict = itemDIct.OrderByDescending(x => x.Key); 라인이 있습니다.
딕셔너리도 마찬가지로 줄세우기가 가능합니다. 현재의 예시에서는
OrderByDescending으로 Key값에 대해 내림차순 정렬 한 것입니다.
딕셔너리의 정렬이 가능하다는 것은 아주 파워풀한 것입니다.
딕셔너리 Value의 멤버 값 하나만 가지고도 Key, Value의 값들을 한데 묶어 이동할 수 있다는 이야기 입니다.
orderedDict의 타입은 아까와 마찬가지로 IOrderedEnumerable<KeyValuePair<int, Item>>타입 입니다.
이상으로 IEnumerable함수들과 Find를 알아봤습니다. 이 함수들을 잘 사용하면 코드 상에서 반복문을 줄여
주게 됩니다. 이를 연습해서 간결하고 보기좋은 코드를 연습해야 겠습니다.
C# List BinarySearch 함수 (0) | 2020.08.16 |
---|---|
MyJsonConverter ver3.0.0 프로젝트 (0) | 2020.04.28 |
Excel 파일 Json으로 변환 프로젝트 (0) | 2020.04.23 |
Excel 파일 Json으로 바꿔주는 프로그램 Ver 2.0 (0) | 2020.04.23 |
Excel파일을 json파일로 변환하는 프로그램 ver 1.0. (0) | 2020.04.22 |