반응형
Notice
Recent Posts
Recent Comments
Link
«   2025/04   »
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
Tags
more
Archives
Today
Total
관리 메뉴

개발꿈나무

[C# 교과서] 13. C# 활용(7) - LINQ 본문

C# 기초

[C# 교과서] 13. C# 활용(7) - LINQ

HYOKYE0NG 2022. 1. 18. 15:42
반응형
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>

 

반응형
Comments