개발꿈나무
[C# 교과서] 13. C# 활용(7) - LINQ 본문
LINQ
LINQ(링크)는 Language INtegrated Query의 약어로, C#에서 컬렉션 형태의 데이터를 가공할 때 유용한 메서드를 많이 제공한다.
확장 메서드 사용하기
닷넷에서 LINQ 확장 메서드를 사용하려면 System.Linq 네임스페이스를 선언해야 한다
- Sum(): 숫자 배열 또는 컬렉션의 합
- Count(); 숫자 배열 또는 컬렉션의 건수
- Average(): 숫자 배열 또는 컬렉션의 평균
- Max(): 최댓값
- Min(): 최솟값
Sum() 메서드로 배열의 합 구하기
using System;
using System.Linq;
class LinqSum
{
static void Main()
{
int[] numbers = { 1, 2, 3 };
int sum = numbers.Sum();
Console.WriteLine($"numbers 배열 요소의 합: {sum}");
}
}
Count() 메서드로 배열의 건수 구하기
using System;
using System.Linq;
class LinqCount
{
static void Main()
{
int[] numbers = { 1, 2, 3 };
int count = numbers.Count();
Console.WriteLine($"{nameof(numbers)} 배열 개수: {count}");
}
}
배열 개수는 배열의 Lenth 속성을 사용해서 구할 수도 있다.
nameof() 연산자: 배열 이름을 문자열로 변환해 줌
Average() 메서드로 배열의 평균 구하기
using System.Linq;
using static System.Console;
class LinqAverage
{
static void Main()
{
int[] numbers = { 1, 3, 4 };
double average = numbers.Average();
WriteLine($"{nameof(numbers)} 배열 요소의 평균: {average:#,###.##}");
}
}
Max() 메서드로 컬렉션의 최댓값 구하기
using System;
using System.Collections.Generic;
using System.Linq;
class LinqMax
{
static void Main()
{
var numbers = new List<int>() { 1, 2, 3 };
int max = numbers.Max();
Console.WriteLine($"{nameof(numbers)} 컬렉션의 최댓값: {max}");
}
}
Min() 메서드로 컬렉션의 최솟값 구하기
using System;
using System.Collections.Generic;
using System.Linq;
class LinqMin
{
static void Main()
{
var numbers = new List<double> { 3.3, 2.2, 1.1 };
var min = numbers.Min();
Console.WriteLine($"{nameof(numbers)} 리스트의 최솟값: {min:.00}");
}
}
화살표 연산자와 람다 식으로 조건 처리
LINQ에서 제공하는 확장 메서드들은 매개변수로 람다 식(lambda expression)을 받는데, 람다 식은 화살표 연산자 또는 람다 연산자라고 하는 화살표 모양의 =? 기호를 사용한다.
람다식의 두가지 형태
- 식 람다: (입력 매개변수) => 식 EX) x => x+1
- 문 람다: (입력 매개변수) => { 문; } EX) x => { return x+1; }
Where() 메서들 IEnumerable<T> 형태의 데이터 가져오기
using System;
using System.Collections.Generic;
using System.Linq;
class LinqWhere
{
static void Main()
{
int[] numbers = { 1, 2, 3, 4, 5 };
IEnumerable<int> newNumbers = numbers.Where(number => number > 3);
foreach (var n in newNumbers)
{
Console.WriteLine(n); //4 5
}
}
}
람다 식 number => number > 3은 매개변수가 들어오면 3보다 큰 데이터만 가져와 IEnumerable<int> 형식의 newNumbers에 대입해서 출력하라는 의미로 3보다 큰 4와 5만 출력된다.
ToList() 메서드로 IEnumerable<T>를 List<T>로 변환하기
using System;
using System.Collections.Generic;
using System.Linq;
class LinqWhereToList
{
static void Main()
{
int[] numbers = { 1, 2, 3, 4, 5 };
List<int> newNumbers = numbers.Where(number => number > 3).ToList();
foreach (var number in newNumbers)
{
Console.WriteLine(number); //4 5
}
}
}
람다 식을 사용하는 Where() 같은 확장 메서드를 호출할 때 반환 형식은 IEnumerable<T> 형식으로
List<T> 형태로 받으려면 ToList() 메서드를 한번 더 호출해야 한다.
All()과 Any() 메서드로 조건 판단하기
- All(): 모든 조건을 만족하면 true, 그렇지 않은 경우 false 반환
- Any(): 하나의 조건이라도 만족하면 true, 그렇지 않은 경우 false 반환
using System;
using System.Linq;
class LinqAll
{
static void Main()
{
bool[] completes = { true, true, true };
// 배열 또는 컬렉션의 모든 항목이 true일때에만 true을 반환
Console.WriteLine(completes.All(c => c == true)); //true
bool[] completes2 = { true, false, true };
// 배열 또는 컬렉션의 하나의 항목이라도 조건을 만족하면 true
Console.WriteLine(completes2.Any(c => c == false)); //true
}
}
Take()와 Skip() 메서드로 필요한 건수의 데이터 가져오기
using System.Linq;
using static System.Console;
class LinqSkipTake
{
static void Main()
{
var data = Enumerable.Range(0, 100); // 0~99
var next = data.Skip(10).Take(5); // 10개 제외하고 5개 가져오기
foreach (var n in next)
{
WriteLine(n); //11 12 13 14 15
}
}
}
Skip() 메서드는 지정된 수만큼 데이터를 제외하고, Take() 메서드는 지정된 수만큼 데이터를 가져온다.
Distinct() 확장 메서드로 중복 제거하기
using System;
using System.Linq;
class LinqDistinct
{
static void Main()
{
var data = Enumerable.Repeat(3, 5); // 3을 5개 저장
var result = data.Distinct(); // Distinct()로 중복 제거
foreach (var num in result)
{
Console.Write("{0}\t", num); // 중복이 제거되어 3 하나만 출력
}
Console.WriteLine(); //3
int[] arr = { 2, 2, 3, 3, 3 }; // 2와 3을 중복해서 배열에 저장
var r = arr.Distinct();
foreach (var num in r)
{
Console.Write($"{num}\t"); // 중복이 제거되어 2와 3 하나씩만 출력
}
Console.WriteLine(); //2 3
}
}
Distinct() 메서드는 컬렉션(시퀀스)에서 중복된 데이터를 제거한다.
데이터 졍렬과 검색
- OrderBy(): 데이터를 오름차순으로 정렬
- OrderByDescending(): 데이터를 내림차순으로 정렬
using System;
using System.Collections.Generic;
using System.Linq;
class LinqOrderBy
{
static void Main()
{
string[] colors = { "Red", "Green", "Blue" };
var sortedColors_asc = colors.OrderBy(name => name);
foreach (var color in sortedColors_asc)
{
Console.WriteLine(color); //Blue Green Red
}
var sortedColors_desc = colors.OrderByDescending(c => c);
foreach (var color in sortedColors_desc)
{
Console.WriteLine(color); //Red Green Blue
}
}
}
확장 메서드 체이닝
using System;
using System.Collections.Generic;
using System.Linq;
class LinqChaining
{
static void Main()
{
List<string> names = new List<string> { ".NET", "C#", "TypeScript" };
// 체이닝: 확장 메서드를 여러 개 사용
var results = names.Where(name => name.Length > 2).OrderBy(n => n);
foreach (var name in results)
{
Console.WriteLine(name); //.NET TypeScript
}
}
}
컬렉션 형태의 데이터에서 Where(), OrderBy() 등 LINQ 확장 메서드를 체이닝으로 여러 번 호출해서 사용할 수 있다.
특정 문자열을 포함하는 컬렉션 가져오기
using System;
using System.Collections.Generic;
using System.Linq;
class LinqSearch
{
static void Main()
{
var colors = new List<string> { "Red", "Green", "Blue" };
var newColors = colors.Where(c => c.Contains("e"));
foreach (var color in newColors)
{
Console.WriteLine(color); //Red, Green, Blue
}
var green = colors.Where(c => c.Contains("ee"));
foreach (var c in green)
{
Console.WriteLine(c); //Green
}
}
}
Contains() 메서드는 특정 문자열을 검색할 수 있다.
Contains() 메서드는 일반적으로 대·소문자를 구분하므로 ToUpper(), ToLower() 메서드를 사용하여 한 쪽으로 바꾼 후
검색하면 대·소문자를 구분하지 않고 값을 검색할 수 있다.
Single()과 SingleOrDefault() 확장 메서드
컬렉션에서 조건에 맞는 값을 단 하나만 가져오는 확장 메서드
- Single(): null 값이면 예외(에러) 발생
- SingleOrDefault(): 값이 없으면 null 값 반환
using System;
using System.Collections.Generic;
using System.Linq;
class LinqSingle
{
static void Main()
{
List<string> colors = new List<string> { "Red", "Green", "Blue" };
string red = colors.Single(c => c == "Red");
Console.WriteLine(red); // "Red"
try
{
// 없는 데이터 요청시 예외 발생
string black = colors.Single(color => color == "Black");
//// 없는 데이터 요청시 null 값 반환
//string black = colors.SingleOrDefault(color => color == "Black");
}
catch (Exception ex)
{
Console.WriteLine("예외 발생: " + ex.Message);
}
}
}
First()와 FirstOrDefault() 확장 메서드
하나 이상의 데이터 중에서 첫 번째 데이터를 가져온다.
- First(): 첫번째 요소가 없으면 에러 발생
- FirstOrDefault(): 첫 번째 요소가 없으면 기본값 반환
using System;
using System.Collections.Generic;
using System.Linq;
class LinqFirst
{
static void Main()
{
List<string> colors = new List<string> { "Red", "Green", "Blue" };
string red = colors.First(c => c == "Red");
Console.WriteLine(red); // "Red"
try
{
// 없는 데이터 요청시 예외 발생
string black = colors.First(color => color == "Black");
//// 없는 데이터 요청시 null 값 반환
//string black = colors.FirstOrDefault(color => color == "Black");
}
catch (Exception ex)
{
Console.WriteLine("예외 발생: " + ex.Message);
}
}
}
메서드 구문과 쿼리 구문
- 메서드 구문(method syntax): Where()같은 메서드를 사용하여 컬렉션을 다루는 방법
- 쿼리 구문(query syntax): from, where, select 같은 키워드를 사용하여 쿼리(query) 형태로 컬렉션을 다루는 방법
using System.Linq;
using static System.Console;
class QuerySyntaxMethodSyntax
{
static void Main()
{
var numbers = Enumerable.Range(1, 10);
// 메소드 구문
WriteLine((from n in numbers where n % 2 == 0 select n).Sum());
WriteLine((from n in numbers where n % 2 == 0 select n).Count());
WriteLine((from n in numbers where n % 2 == 0 select n).Average());
WriteLine((from n in numbers where n % 2 == 0 select n).Max());
WriteLine((from n in numbers where n % 2 == 0 select n).Min());
// 쿼리 구문: arr 배열에서 짝수 데이터만 배열로 가져오기
var q =
from n in numbers
where n % 2 == 0
select n;
foreach (var i in q)
{
Console.WriteLine($"{i}"); // 2, 4, 6, 8, 10
}
}
}
Select() 확장 메서드를 사용하여 새로운 형태로 가공하기
Select() 확장 메서드는 컬렉션에서 새로운 형태의 데이터로 만들어 사용할 수 있다.
using System;
using System.Collections.Generic;
using System.Linq;
class LinqSelect
{
static void Main()
{
var names = new List<string> { "홍길동", "백두산", "임꺽정" };
// Select() 확장 메서드에서 익명 형식을 사용하기에 var로 받아야 함
var nameObjects = names.Select(n => new { Name = n });
foreach (var name in nameObjects)
{
Console.WriteLine(name.Name);
}
}
}
numbers 배열에서 데이터를 하나씩 조회해서 각 값을 곱한 새로운 형태인 nums 컬랙션을 Select() 확장 메서드로
생성할 수 있다. Select()의 결괏값은 클래스 이름이 정해지지 않은 익명 형식이기 때문에
var 키워드와 함께 사용해야 한다.
ForEach() 메서드로 반복 출력하기
ForEach() 메서드를 사용하면 List<T> 형태를 갖는 리스트 값만큼 반복하는 코드를 작성할 수 있다.
using System;
using System.Collections.Generic;
using System.Linq;
class LinqForEach
{
static void Main()
{
var numbers = new List<int>() { 10, 20, 30, 40, 50 };
numbers.Where(n => n <= 20).ToList().ForEach(n => Console.WriteLine(n));
var names = new List<string>() { "RedPlus", "Taeyo" };
names.ForEach(n => Console.WriteLine(n));
}
}
Zip 확장 메서드
Zip 확장 메서드는 관련 있는 시퀀스(컬렉션) 2개를 묶어 출력한다.
using System;
using System.Linq;
class LinqZip
{
static void Main()
{
int[] numbers = { 1, 2, 3 };
string[] words = { "하나", "둘" };
var numbersAndWords =
numbers.Zip(words, (first, second) => first + "-" + second);
foreach (var item in numbersAndWords)
{
Console.WriteLine(item); // 1-하나, 2-둘
}
}
}
<Reference>
'C# 기초' 카테고리의 다른 글
[C# 교과서] 15. C# 활용(9) - 개체 만들기 (0) | 2022.01.19 |
---|---|
[C# 교과서] 14. C# 활용(8) - 알고리즘과 절차 지향 프로그래밍 (0) | 2022.01.19 |
[C# 교과서] 12. C# 활용(6) - 널(null) 다루기 (0) | 2022.01.18 |
[C# 교과서] 10. C# 활용(4) - 컬렉션 사용하기 (0) | 2022.01.17 |
[C# 교과서] 9. C# 활용(3) - 예외 처리하기 (0) | 2022.01.17 |