본문 바로가기
개발/Unity 내일배움캠프 TIL

C# LINQ 간단 정리

by 석시 2023. 8. 25.



C#을 공부하게 되면 한 번쯤은 써봤을 LINQ.

각종 컨테이너들을 다룰 수 있는 강력한 기능을 탑재한 도구라서 그런 것 같다.

사실 LINQ는 쿼리 언어의 확장으로써 코드에서도 쿼리를 던질 수가 있다. 데이터베이스에서 데이터를 쿼리하는 방식처럼 C#의 컨테이너들을 다룰 수 있도록 한 기능이다.

Language INtegrated Query

이름도 사실 쿼리이다!

나는 컨테이너 객체에다만 사용해봤기 때문에 몰랐지만, 어지간한 데이터 소스에다가 사용이 가능하다.

객체 (컬렉션), 데이터베이스, XML 문서, 심지어는 csv까지 사용이 가능하다.

LINQ를 이용한 쿼리 작성법

// 데이터 소스 정의 (컬렉션)
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };

// 쿼리 작성 (선언적인 구문)
var evenNumbers = from num in numbers
                  where num % 2 == 0
                  select num;

// 쿼리 실행 및 결과 처리
foreach (var num in evenNumbers)
{
    Console.WriteLine(num);
}

  • var 키워드는 결과 값의 자료형을 자동으로 추론한다.
  • from 절에서는 데이터 소스를 지정한다.
  • where 절은 선택적으로 사용하며, 조건식을 지정하여 데이터를 필터링한다.
  • orderby 절은 선택적으로 사용하며, 정렬 방식을 지정한다.
  • select 절은 선택적으로 사용하며, 조회할 데이터를 지정한다.

주의점

데이터베이스 쿼리와 유사한 방식이기 때문에 필터링, 정렬, 그룹화, 조인 등의 다양한 작업을 수행할 수 있다.

이러한 작업들 모두 쿼리를 던지고 필터링을 하다보니까 데이터가 많이 커지고 필터링해야 되는 조건들이 많아진다면 성능이 급감하기 때문에

LINQ를 사용할 때는 데이터가 한 번 가공이 되어있는 것이 매우 중요하다.

쿼리 역시 간결하면 간결할수록 좋다.

대표 메서드 정리

1. Aggregate()

string[] fruits = { "apple", "mango", "orange", "passionfruit", "grape" };

// Determine whether any string in the array is longer than "banana".
string longestName =
    fruits.Aggregate("banana",
                    (longest, next) =>
                        next.Length > longest.Length ? next : longest,
    // Return the final result as an upper case string.
                    fruit => fruit.ToUpper());

Console.WriteLine(
    "The fruit with the longest name is {0}.",
    longestName);

// This code produces the following output:
//
// The fruit with the longest name is PASSIONFRUIT.

데이터를 내가 원하는대로 누적하는 메서드이다.

인자로 넣어주는 함수에 따라 컬렉션을 순회하면서 함수의 결과값을 계속 누적해주는 함수이다.

위 내용은 “banana”라는 시드부터 시작해서 fruits를 순회하는데, 둘 중 더 큰 값을 longest에 저장하는 것을 반복한 후, 그 결과에 ToUpper()를 적용해서 반환해주는 함수를 거치는 것이다.

2. All(), Any()

All()은 컬렉션의 모든 요소가 특정 조건에 맞는지 확인하는 메서드이다.

인자로 검사하려는 컬렉션과 조건을 받는데 그 조건은 bool형을 반환하는 함수 형태면 된다.

bool allStartWithB = pets.All(pet => pet.Name.StartsWith("B"));
IEnumerable<string> names = from person in people
                                where person.Pets.All(pet => pet.Age > 5)
                                select person.LastName;

Any()의 경우는 All()과 같은 방식으로 조건을 만족하는 요소가 단 하나라도 있는지 확인한다.

string[] fruits = { "apple", "mango", "orange", "passionfruit", "grape" };
 
bool ContainLength5 = fruits.Any((element) => element.Length == 5);

3. Average()

List<int> grades = new List<int> { 78, 92, 100, 37, 81 };

double average = grades.Average();

Console.WriteLine("The average grade is {0}.", average);

// This code produces the following output:
//
// The average grade is 77.6.

컬렉션 전체의 평균을 계산한다.

4. Concat()

using System;

public class Example
{
    public static void Main()
    {
        // Make an array of strings. Note that we have included spaces.
        string [] s = { "hello ", "and ", "welcome ", "to ",
                        "this ", "demo! " };

        // Put all the strings together.
        Console.WriteLine(string.Concat(s));

        // Sort the strings, and put them together.
        Array.Sort(s);
        Console.WriteLine(string.Concat(s));
    }
}
// The example displays the following output:
//       hello and welcome to this demo!
//       and demo! hello this to welcome

컬렉션을 하나로 이어붙여주는 메서드이다.

두 개말고 세 개 이상 요소들도 붙여줄 수가 있다.

참고로 해당 함수로 다른 언어의 string multiplication을 구현해줄 수 있다.

다음은 휴대폰 뒷 4자리 빼고 전부 *로 만들어주는 함수이다.

public string solution(string phone_number) {
    return String.Concat(Enumerable.Repeat("*", phone_number.Length - 4)) + phone_number.Substring(phone_number.Length - 4);
}

5. Contains()

string[] fruits = { "apple", "mango", "orange", "passionfruit", "grape" };
 
print(fruits.Contains("apple")); //prints true

컬렉션에 해당 요소가 들어있는지 확인한다.

6. Count()

string[] fruits = { "apple", "mango", "orange", "passionfruit", "grape" };
 
var count = fruits.Count(e => e.Contains('p'));

print(count); //prints 3

컬렉션의 요소 개수를 확인해준다.

7. Distinct()

string[] fruits = { "apple", "grape", "orange", "apple", "grape" };
 
foreach (var fruit in fruits.Distinct())
{
    print(fruit);
}

//prints following output
//apple
//grape
//orange

중복 요소를 전부 제거해준다.

8. Except()

string[] fruitsA = { "apple", "grape", "orange", "pineapple", "passionfruit" };
 
string[] fruitsB = { "orange", "pineapple", "passionfruit", "banana" };

foreach (var fruit in fruitsA.Except(fruitsB))
{
    print(fruit);
}

//prints following output
//apple
//grape

A.Except(B)면 B 컬렉션에 있는 요소를 제외한 A 컬렉션의 요소를 반환한다.

9. OrderBy()

IEnumerable<Pet> query = pets.OrderBy(pet => pet.Age);

foreach (Pet pet in query)
{
    Console.WriteLine("{0} - {1}", pet.Name, pet.Age);
}
/*
 This code produces the following output:

 Whiskers - 1
 Boots - 4
 Barley - 8
*/

정렬 함수이다. 오름차순인데, 내림차순 정렬을 원한다면 OrderByDescending()을 사용하자.

10. 그 외 것들

Sum(): 컬렉션 요소의 합

Reverse(): 컬렉션 요소 순서를 뒤집는다.

Repeat(): 반복되는 값의 요소를 가진 컬렉션 생성

Min(), Max(): 가장 작은 요소 또는 큰 요소를 반환한다.

Join(): 관계형 데이터베이스에서 테이블 행들을 합치는 메서드인데, 해당 기능은 DB의 파트이니 시간나면 따로 찾아보도록 하자.


Uploaded by N2T