글 작성자: Doublsb

본 글은 공부하며 작성하였습니다. 정보를 얻으실 때 사실과 다르거나 부정확한 정보가 들어있을 수도 있습니다.

 

LINQ의 Method를 정리한다. 예제는 마이크로소프트 문서 또는 본인 코드.


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.

Aggregate(초기값, (인자) => 식) 과 같은 형태다. 물론 Aggregate((인자) => 식)으로도 가능.

 

예제에서는 "banana"를 초기값으로 두고, fruits의 각 요소마다 반복을 했다. longestName에 가장 긴 string인 "passionfruit"가 반환된다.


All

모든 요소가 특정 조건에 맞는지 확인한다.

Pet[] pets = { new Pet { Name="Barley", Age=10 },
                   new Pet { Name="Boots", Age=4 },
                   new Pet { Name="Whiskers", Age=6 } };

    // Determine whether all pet names 
    // in the array start with 'B'.
    bool allStartWithB = pets.All(pet =>
                                      pet.Name.StartsWith("B"));

 


Any

하나라도 조건에 맞는 요소가 있는지 확인한다.

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

        bool ContainLength5 = fruits.Any((element) => element.Length == 5);

AsEnumerable

시퀀스를 IEnumerable<T>의 형태로 반환한다.


Average

숫자 값 시퀀스의 평균을 계산한다.

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

        var result = fruits.Average(element => element.Length);
int[] numbers = { 2, 5, 10, 200, 30 };

        print(numbers.Average()); //prints 49.4

Cast

IEnumerable의 각 요소를 지정된 형식으로 캐스팅한다. 쉽게 말하자면 형식이 없던 것들에게 타입을 지정해주는 것이다.

ArrayList fruits = new ArrayList();
        fruits.Add("apple");
        fruits.Add("mango");
        fruits.Add("orange");

        IEnumerable enumerable = fruits.Cast<string>();

형식이 없던 List에 캐스트를 수행하지 않으면, 아래와 같은 foreach문을 돌릴 수 없다.

foreach (string fruit in query)
{
    Console.WriteLine(fruit);
}

Concat

두 시퀀스를 연결한다.

Pet[] cats = GetCats();
Pet[] dogs = GetDogs();

IEnumerable<string> query =
	cats.Select(cat => cat.Name).Concat(dogs.Select(dog => dog.Name));

Contains

시퀀스에 지정한 요소가 들어 있는지 확인한다.

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

print(fruits.Contains("apple")); //prints true

Count

시퀀스의 수를 반환한다.

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

        var count = fruits.Count(e => e.Contains('p'));

        print(count); //prints 3

DefaultIfEmpty

시퀀스가 비어 있으면 기본값을 할당하여 반환한다.

List<string> fruits = new List<string>();

        foreach (var fruit in fruits.DefaultIfEmpty("default"))
        {
            print(fruit);
        }
        
        //prints default
List<string> fruits = new List<string>();
        fruits.Add("banana");

        foreach (var fruit in fruits.DefaultIfEmpty("default"))
        {
            print(fruit);
        }
        
        //prints banana

Distinct

중복된 요소들을 제외한 시퀀스를 반환한다.

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

        foreach (var fruit in fruits.Distinct())
        {
            print(fruit);
        }
        
        //prints following output
        //apple
        //grape
        //orange

ElementAt

지정한 인덱스의 요소를 반환한다. 예제는 굳이 왜 ElementAt을 쓰는 건가 싶지만.

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

        int randomIndex = Random.Range(0, fruits.Length);

        print(fruits[randomIndex]);

        print(fruits.ElementAt(randomIndex));

ElementAtOrDefault

지정한 인덱스의 요소를 반환한다. 만약 인덱스가 범위를 벗어나면 기본값을 반환한다. 아니, 예외처리 코드를 굳이 작성할 필요 없는 이런 갓 메소드가...

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

        print(fruits.ElementAtOrDefault(10));
        
        //prints null

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

First

시퀀스의 첫번째 요소를 반환한다.

int[] numbers = { 9, 34, 65, 92, 87, 435, 3, 54, 
                    83, 23, 87, 435, 67, 12, 19 };

int first = numbers.First(number => number > 80);

Console.WriteLine(first);
//prints 92

FirstOrDefault

시퀀스의 첫번째 요소를 반환한다. 없으면 기본값을 반환한다.

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

        print(fruitsA.FirstOrDefault(e => e.Length > 20));
        //prints Null

GroupBy

시퀀스의 요소를 그룹화한다.

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

IEnumerable<IGrouping<int, string>> groupFruits = fruits.GroupBy(e => e.Length);

foreach (var group in groupFruits)
{
	foreach (var item in group)
	{
		print($"length {group.Key} : {item}");
	}
}


GroupJoin

키가 같은지 여부에 따라 두 시퀀스의 요소를 연관시키고 결과를 그룹화한다.

내용이 길어질 것 같아 다른 포스트로 작성할 예정. (이후 해당 포스트 이곳에 링크)


Intersect

두 시퀀스의 교집합을 구한다.

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

        string[] fruitsB = { "banana", "pineapple", "mango" };

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

        //prints pineapple

Join

두 시퀀스를 하나로 합친다... 라고 말하면 완전히 적절하지는 않은 것 같다. 굳이 표현하자면 DB의 내부 조인이다.

예제가 또 길어지므로 다른 포스트로 분리하겠다. Join/GroupJoin만 따로 다뤄야 할 듯.


Last, LastOrDefault

시퀀스의 마지막 요소를 반환한다. 예제는 First/FirstOrDefault와 거의 동일하므로 생략.


Max

시퀀스의 최대값을 반환한다. 아래 예제는 string 값이 아니라 int 값을 반환한다. 주의.

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

        print(fruitsA.Max(e => e.Length));

        //prints 12

Min

시퀀스의 최소값을 반환한다. Max와 별 차이 없으므로 넘어간다.


OfType

시퀀스에서 지정한 타입만 걸러낸다.

System.Collections.ArrayList fruits = new System.Collections.ArrayList(4);
fruits.Add("Mango");
fruits.Add("Orange");
fruits.Add(3.0);
fruits.Add("Banana");

// Apply OfType() to the ArrayList.
IEnumerable<string> query1 = fruits.OfType<string>();

// This code produces the following output:
//
// Elements of type 'string' are:
// Mango
// Orange
// Banana

OrderBy

시퀀스의 요소를 오름차순으로 정렬한다.

Pet[] pets = { new Pet { Name="Barley", Age=8 },
                   new Pet { Name="Boots", Age=4 },
                   new Pet { Name="Whiskers", Age=1 } };

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

    foreach (Pet pet in query)
    {
        Console.WriteLine("{0} - {1}", pet.Name, pet.Age);
    }

OrderByDescending

시퀀스의 요소를 내림차순으로 정렬한다. 생략.


Range

지정된 범위 내의 정수 시퀀스를 생성한다. 와---우.

 

// Generate a sequence of integers from 1 to 10 
// and then select their squares.
IEnumerable<int> squares = Enumerable.Range(1, 10).Select(x => x * x);

foreach (int num in squares)
{
    Console.WriteLine(num);
}

/*
 This code produces the following output:

 1
 4
 9
 16
 25
 36
 49
 64
 81
 100
*/

Repeat

반복되는 단일 값이 들어 있는 시퀀스를 생성한다.

IEnumerable<string> strings =
    Enumerable.Repeat("I like programming.", 8);

foreach (String str in strings)
{
    Console.WriteLine(str);
}

/*
 This code produces the following output:

 I like programming.
 I like programming.
 I like programming.
 I like programming.
 I like programming.
 I like programming.
 I like programming.
 I like programming.
*/

Reverse

시퀀스의 요소 순서를 반전한다. 명확한 설명이므로 굳이 예제는 안 가져 왔음.


Select

시퀀스의 각 요소를 가져와서 입맛대로 가공한다.

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

        var result = fruitsA.Select((e, index) => $"[Index = {index} / {e}]" );

        foreach (var item in result)
        {
            print(item);
        }


SelectMany

Select가 하나의 시퀀스에서 값을 가공한다면, SelectMany는 다중 시퀀스에서 값을 가공한다고 생각하면 된다.

string[] fruitsA = { "apple", "grape" };
        int[] fruitsB = { 0, 1, 2, 3 };

        var result = fruitsA.SelectMany(fruitA => fruitsB, (a, b) => new { a, b });

        foreach (var item in result)
        {
            print($"{item.a}, {item.b}");
        }
        //prints following output
        //apple, 0
        //apple, 1
        //apple, 2
        //apple, 3
        //grape, 0
        //grape, 1
        //grape, 2
        //grape, 3

예제는 fruitsA와 fruitsB에서 값을 가져와 가공하는 모습을 보여주고 있다.


SequenceEqual

두 시퀀스의 요소가 같은지 확인한다.

Pet pet1 = new Pet { Name = "Turbo", Age = 2 };
    Pet pet2 = new Pet { Name = "Peanut", Age = 8 };

    // Create two lists of pets.
    List<Pet> pets1 = new List<Pet> { pet1, pet2 };
    List<Pet> pets2 = new List<Pet> { pet1, pet2 };

    bool equal = pets1.SequenceEqual(pets2);

Single

시퀀스의 특정 단일 요소를 반환한다. 조건에 맞는 요소가 둘 이상이면 예외가 발생한다.

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

string fruit1 = fruits.Single(fruit => fruit.Length > 10);

Console.WriteLine(fruit1);
//prints passionfruit

SingleOrDefault

Single인데, 특정 단일 요소가 존재하지 않으면 기본값을 반환한다. 생략.


Skip

시퀀스에서 지정된 수의 요소를 건너뛴 다음 나머지 요소를 반환한다. 다른 LinQ 메소드랑 같이 쓰는 방식이다.

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

        var result = fruitsA.Where((e) => e.Length > 4).Skip(3);

        foreach (var item in result)
        {
            print(item);
        }
        //prints following output
        //pineapple
        //passionfruit

SkipLast

Skip이 앞에서부터 건너뛰었다면, SkipLast는 뒤에서부터 지정된 수만큼 없애는 방식.


SkipWhile

지정된 조건이 true인 동안은 skip한다.

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

        var result = fruitsA.SkipWhile((e) => e.Length < 6);

        foreach (var item in result)
        {
            print(item);
        }
        //prints following output
        //orange
        //pineapple
        //passionfruit
        //fry

Sum

숫자 값 시퀀스의 합을 계산한다. 생략.


Take

시퀀스 앞부터 지정된 개수만큼만 반환한다. 3을 인자로 주면, 앞에서부터 3개만 가져온다.

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

        var result = fruitsA.Take(2);

        foreach (var item in result)
        {
            print(item);
        }
        //prints following output
        //apple
        //grape

TakeLast

시퀀스 뒤부터 지정된 개수만큼만 반환한다.


TakeWhile

지정된 조건이 true인 동안 시퀀스의 요소를 반환한다.

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

        var result = fruitsA.TakeWhile((e) => e.Length == 5);

        foreach (var item in result)
        {
            print(item);
        }
        //prints following output
        //apple
        //grape

ThenBy

시퀀스의 요소를 정렬했던 걸 다시 오름차순으로 정렬한다. ...예제를 보자.

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

// Sort the strings first by their length and then 
//alphabetically by passing the identity selector function.
IEnumerable<string> query =
    fruits.OrderBy(fruit => fruit.Length).ThenBy(fruit => fruit);

foreach (string fruit in query)
{
    Console.WriteLine(fruit);
}

/*
    This code produces the following output:

    apple
    grape
    mango
    banana
    orange
    blueberry
    raspberry
    passionfruit
*/

fruits를 길이 순으로 정렬한 뒤에, 알파벳 순으로 재정렬하였다.


ThenByDescending

시퀀스의 요소를 정렬했던 걸 다시 내림차순으로 정렬한다.


ToArray

배열 형태로 변환한다.


ToDictionary

딕셔너리 형태로 변환한ㄷ.... 뭐라고?

class Package
{
    public string Company { get; set; }
    public double Weight { get; set; }
    public long TrackingNumber { get; set; }
}

public static void ToDictionaryEx1()
{
    List<Package> packages =
        new List<Package>
            { new Package { Company = "Coho Vineyard", Weight = 25.2, TrackingNumber = 89453312L },
              new Package { Company = "Lucerne Publishing", Weight = 18.7, TrackingNumber = 89112755L },
              new Package { Company = "Wingtip Toys", Weight = 6.0, TrackingNumber = 299456122L },
              new Package { Company = "Adventure Works", Weight = 33.8, TrackingNumber = 4665518773L } };

    // Create a Dictionary of Package objects, 
    // using TrackingNumber as the key.
    Dictionary<long, Package> dictionary =
        packages.ToDictionary(p => p.TrackingNumber);

    foreach (KeyValuePair<long, Package> kvp in dictionary)
    {
        Console.WriteLine(
            "Key {0}: {1}, {2} pounds",
            kvp.Key,
            kvp.Value.Company,
            kvp.Value.Weight);
    }
}

/*
 This code produces the following output:

 Key 89453312: Coho Vineyard, 25.2 pounds
 Key 89112755: Lucerne Publishing, 18.7 pounds
 Key 299456122: Wingtip Toys, 6 pounds
 Key 4665518773: Adventure Works, 33.8 pounds
*/

아이고 자동으로 키를 뽑아내는 줄 알고 놀랐지 않은가 ^^ 키는 내가 지정해주는 거였다.


ToHashSet

HashSet 형태로 변환한다.

 


ToList

List 형태로 변환한다.


ToLookup

Lookup 형태로 변환한다. 이 자료형은 처음 본다.


Union

두 시퀀스의 합집합을 구한다. 생략.


Where

조건자에 따라 값의 시퀀스를 필터링한다. 가장 자주 쓰는 LinQ 메서드가 되지 않을까 싶다.

List<string> fruits =
    new List<string> { "apple", "passionfruit", "banana", "mango", 
                    "orange", "blueberry", "grape", "strawberry" };

IEnumerable<string> query = fruits.Where(fruit => fruit.Length < 6);

foreach (string fruit in query)
{
    Console.WriteLine(fruit);
}
/*
 This code produces the following output:

 apple
 mango
 grape
*/

Zip

지정된 함수를 두 시퀀스의 해당 요소에 적용하여 결과 시퀀스를 만든다.

int[] numbers = { 1, 2, 3, 4 };
string[] words = { "one", "two", "three" };

var numbersAndWords = numbers.Zip(words, (first, second) => first + " " + second);

foreach (var item in numbersAndWords)
    Console.WriteLine(item);

// This code produces the following output:

// 1 one
// 2 two
// 3 three

 

반응형