집게사장의 꿈

C# LINQ 쿼리 키워드 본문

기타/C#

C# LINQ 쿼리 키워드

Krapboss 2024. 7. 21. 20:12

https://learn.microsoft.com/ko-kr/dotnet/csharp/language-reference/keywords/query-keywords

From
"데이터 소스 참조할 변수를 지정하는 것"

 

 

기본

var lowNums = from num in numbers
            where num < 5
            select num;

 

num : int 타입 참조받을 데이터

numbers : 참조할 데이터 컬렉션


 

복합 From

		List<Student> students =
        [
           new Student {LastName="Omelchenko", Scores= [97, 72, 81, 60]},
           new Student {LastName="O'Donnell", Scores= [75, 84, 91, 39]},
           new Student {LastName="Mortensen", Scores= [88, 94, 65, 85]},
           new Student {LastName="Garcia", Scores= [97, 89, 85, 82]},
           new Student {LastName="Beebe", Scores= [35, 72, 91, 70]}
        ];


        var scoreQuery = from student in students
                         from score in student.Scores
                            where score > 90
                            select new { Last = student.LastName, score };

 

설명 : strudents에 있는 Scores 배열값을 참조하기 위한 것


 

From Join

'For 반복문을 사용한 결과와 같음'

	char[] upperCase = ['A', 'B', 'C'];
        char[] lowerCase = ['x', 'y', 'z'];
        
        //Cross Join
        var joinQuery1 =
            from upper in upperCase
            from lower in lowerCase
            select new { upper, lower };
            
        //Filtered non-equijoin
        var joinQuery2 =
            from lower in lowerCase
            where lower != 'x'
            from upper in upperCase
            select new { lower, upper };
            
        /* Output:
        Cross join:
        A is matched to x
        A is matched to y
        A is matched to z
        B is matched to x
        B is matched to y
        B is matched to z
        C is matched to x
        C is matched to y
        C is matched to z
        Filtered non-equijoin:
        y is matched to A
        y is matched to B
        y is matched to C
        z is matched to A
        z is matched to B
        z is matched to C
        */

 

 

 

 


 

Where
"데이터 소스에서 어떠한 요소가 쿼리식에 반환될지를 정하는 것"
'Bool 조건을 적용하여 반환될 요소를 지정할 수 있다.'

 

기본

//5보다 작은 값을 찾아서 반환
var queryLowNums =
    from num in numbers
    where num < 5
    select num;

 


 

Where 중첩

//&& || 연산을 사용
    var queryLowNums2 =
        from num in numbers
        where num < 5 && num % 2 == 0
        select num;
//Where을 중첩
    var queryLowNums3 =
        from num in numbers
        where num < 5
        where num % 2 == 0
        select num;

// Output:
// 4 2 0
// 4 2 0

 

instance || static 함수를 사용한 Where연산

var queryEvenNums =
            from num in numbers
            where IsEven(num)
            select num;
            
    // Method may be instance method or static method.
    static bool IsEven(int i) => i % 2 == 0;

 

 


 

Select
"값이 생성되는 형식을 지정하는 것"

 

기본

//score을 그래도 사용하는 것
IEnumerable<int> queryHighScores =
            from score in Scores
            where score > 80
            select score;

 


 

복합

 

    ///클래스 --------------------
    public class Student
    {
        public required string First { get; init; }
        public required string Last { get; init; }
        public required int ID { get; init; }
        public required List<int> Scores;
        public ContactInfo? GetContactInfo(SelectSample2 app, int id)
        {
            ContactInfo? cInfo =
                (from ci in app.contactList
                where ci.ID == id
                select ci)
                .FirstOrDefault();

            return cInfo;
        }

        public override string ToString() => $"{First} {Last}:{ID}";
    }

    public class ContactInfo
    {
        public required int ID { get; init; }
        public required string Email { get; init; }
        public required string Phone { get; init; }
        public override string ToString() => $"{Email},{Phone}";
    }
    
    ///데이터 ---------------------------------
    List<Student> students =
    [
         new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List<int>() {97, 92, 81, 60}},
         new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new List<int>() {75, 84, 91, 39}},
         new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List<int>() {88, 94, 65, 91}},
         new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List<int>() {97, 89, 85, 82}},
    ];

    // Separate data source for contact info.
    List<ContactInfo> contactList =
    [
        new ContactInfo {ID=111, Email="SvetlanO@Contoso.com", Phone="206-555-0108"},
        new ContactInfo {ID=112, Email="ClaireO@Contoso.com", Phone="206-555-0298"},
        new ContactInfo {ID=113, Email="SvenMort@Contoso.com", Phone="206-555-1130"},
        new ContactInfo {ID=114, Email="CesarGar@Contoso.com", Phone="206-555-0521"}
    ];
    
    /// 쿼리---------------------------------
    
        //Student 클래스에서 ContactInfo클래스를 반환받아 데이터를 선택
        IEnumerable<ContactInfo> studentQuery3 =
            from student in app.students
            where student.ID > 111
            select student.GetContactInfo(app, student.ID);
            
        //새로운 ScoreInfo 쿨래스 생성
        IEnumerable<ScoreInfo> studentQuery8 =
            from student in app.students
            where student.ID > 111
            select new ScoreInfo
            {
                Average = student.Scores.Average(),
                ID = student.ID
            };
            
         //임시 데이터 타입을 생성
         var studentQuery7 =
            from student in app.students
            where student.ID > 111
            select new { student.First, student.Last };
         
         //Join 연산을 사용 후 데이터를 선택
         IEnumerable<ContactInfo> studentQuery9 =
            from student in app.students
            where student.Scores.Average() > 85
            join ci in app.contactList on student.ID equals ci.ID
            select ci;

 

 


 

Group
"그룹의 키 값과 일치하는 0개 이상의 항목으로 그룹화된 데이터를 반환하는 것"

반환 형식 : IGrouping<TKey,TElement>

 

기본

// Query variable is an IEnumerable<IGrouping<char, Student>>
var studentQuery1 =
    from student in students
    group student by student.Last[0];

group : 그룹을 만들 데이터

by : 그룹의 분류가 될 키값

 

 

참조 방법

foreach (IGrouping<char, Student> studentGroup in studentQuery2)
{
     Console.WriteLine(studentGroup.Key);
     // Explicit type for student could also be used here.
     foreach (var student in studentGroup)
     {
         Console.WriteLine("   {0}, {1}", student.Last, student.First);
     }
 }

 


 

into

var studentQuery2 =
    from student in students
    group student by student.Last[0] into g
    orderby g.Key
    select g;

그룹화된 내용을 g라는 이름으로 임시 식별자로 지정한다.

 

 

 

Bool로 구분한 그룹

	//Bool 값을 사용한 그룹화
	var booleanGroupQuery =
            from student in students
            group student by student.Scores.Average() >= 80; //pass or fail!

 

 

숫자 범위에 따른 그룹화

	var studentQuery =
            from student in students
            let avg = (int)student.Scores.Average()
            group student by (avg / 10) into g
            orderby g.Key
            select g;

 

 

 


 

OrderBy
"시퀀스 또는 하위 시퀀스(그룹)가 오름차순 또는 내림차순으로 정렬"

 

기본

        //오름차순
        IEnumerable<string> sortAscendingQuery =
            from fruit in fruits
            orderby fruit //"ascending" is default
            select fruit;

        // 내림차순
        IEnumerable<string> sortDescendingQuery =
            from w in fruits
            orderby w descending
            select w;

 


Let
"쿼리 하위식에 대한 값을 저장하여 사용할 때"

 

기본

        string[] strings =
        [
            "A penny saved is a penny earned.",
            "The early bird catches the worm.",
            "The pen is mightier than the sword."
        ];

        // Split the sentence into an array of words
        // and select those whose first letter is a vowel.
        var earlyBirdQuery =
            from sentence in strings
            let words = sentence.Split(' ')
            from word in words
            let w = word.ToLower()
            where w[0] == 'a' || w[0] == 'e'
                || w[0] == 'i' || w[0] == 'o'
                || w[0] == 'u'
            select word;

        // Execute the query.
        foreach (var v in earlyBirdQuery)
        {
            Console.WriteLine("\"{0}\" starts with a vowel", v);
        }

 

 


 

Join
"두 개의 소스 시퀀스를 이용하여 연결하는 것"


[예를 들어 ]
join 절은 모두 동일한 지역에 있는, 해당 제품의 공급업체 및 구매자 목록을 만드는 데 사용


join 절은 특수한 equals 키워드를 사용하여 지정된 키가 같은지 비교
join 절로 수행된 모든 조인은 동등 조인

 

 

데이터 정의

    List<Category> categories =
    [
        new Category {Name="Beverages", ID=001},
        new Category {Name="Condiments", ID=002},
        new Category {Name="Vegetables", ID=003},
        new Category {Name="Grains", ID=004},
        new Category {Name="Fruit", ID=005}
    ];

    // Specify the second data source.
    List<Product> products =
    [
      new Product {Name="Cola",  CategoryID=001},
      new Product {Name="Tea",  CategoryID=001},
      new Product {Name="Mustard", CategoryID=002},
      new Product {Name="Pickles", CategoryID=002},
      new Product {Name="Carrots", CategoryID=003},
      new Product {Name="Bok Choy", CategoryID=003},
      new Product {Name="Peaches", CategoryID=005},
      new Product {Name="Melons", CategoryID=005},
    ];

 

 

내부 조인

같은 ID에 해당하는 모든 경우의 수를 탐색 후 조합하는 것과 동일

for문을 돌려 category.ID와 prod.CategoryID가 동일하다면 해당 카테고리 아이디와 음료 이름을 조합하는 것이라고 생각해도 좋다.

var innerJoinQuery =
           from category in categories
           join prod in products on category.ID equals prod.CategoryID
           select new { Category = category.ID, Product = prod.Name };

        Console.WriteLine("InnerJoin:");
        // Execute the query. Access results
        // with a simple foreach statement.
        foreach (var item in innerJoinQuery)
        {
            Console.WriteLine("{0,-10}{1}", item.Product, item.Category);
        }
        Console.WriteLine("InnerJoin: {0} items in 1 group.", innerJoinQuery.Count());
        Console.WriteLine(System.Environment.NewLine);
    /*
    InnerJoin:
    Cola      1
    Tea       1
    Mustard   2
    Pickles   2
    Carrots   3
    Bok Choy  3
    Peaches   5
    Melons    5
    InnerJoin: 8 items in 1 group.
	*/

 

prod : products 데이터를 참조받아올 데이터

products : 원본 데이터 소스

 

on : 조건

equals : 좌 우 데이터가 동일한지 판단

 

 


 

그룹 조인

 

왼쪽 소스 시퀀스의 요소를 오른쪽 소스 시퀀스에서 일치하는 하나 이상의 요소와 연결하는 계층적 결과 시퀀스를 생성

일치하는 항목이 없을 경우 join 절은 해당 항목에 대해 빈 배열을 생성

따라서 결과 시퀀스가 그룹으로 구성된다는 점을 제외하면 그룹 조인은 기본적으로 내부 동등 조인

 

//category.ID equals prod.CategoryID 통해 생성된 join은 하나의 그룹이 되며,
//prod가 그룹 내부 요소로 지정된다.
var groupJoinQuery =
           from category in categories
           join prod in products on category.ID equals prod.CategoryID into prodGroup
           select prodGroup;

        // Store the count of total items (for demonstration only).
        int totalItems = 0;

        Console.WriteLine("Simple GroupJoin:");

        //그룹 아이템은 products를 기준
        foreach (var prodGrouping in groupJoinQuery)
        {
            Console.WriteLine("Group:");
            foreach (var item in prodGrouping)
            {
                totalItems++;
                Console.WriteLine("   {0,-10}{1}", item.Name, item.CategoryID);
            }
        }
    
    /*
    Unshaped GroupJoin:
    Group:
        Cola      1
        Tea       1
    Group:
        Mustard   2
        Pickles   2
    Group:
        Carrots   3
        Bok Choy  3
    Group:
    Group:
        Peaches   5
        Melons    5
    Unshaped GroupJoin: 8 items in 5 unnamed groups
    */

 

 


 

그룹 내부 조인

 

        //prod 로 그룹화된 내용을 재정의
        var groupJoinQuery2 =
            from category in categories
            orderby category.ID
            join prod in products on category.ID equals prod.CategoryID into prodGroup
            select new
            {
                Category = category.Name,
                Products = from prod2 in prodGroup
                           orderby prod2.Name
                           select prod2
            };

        //Console.WriteLine("GroupInnerJoin:");
        int totalItems = 0;

        Console.WriteLine("GroupInnerJoin:");
        foreach (var productGroup in groupJoinQuery2)
        {
            Console.WriteLine(productGroup.Category);
            foreach (var prodItem in productGroup.Products)
            {
                totalItems++;
                Console.WriteLine("  {0,-10} {1}", prodItem.Name, prodItem.CategoryID);
            }
        }
        
        /*  
        GroupInnerJoin:
        Beverages
            Cola       1
            Tea        1
        Condiments
            Mustard    2
            Pickles    2
        Vegetables
            Bok Choy   3
            Carrots    3
        Grains
        Fruit
            Melons     5
            Peaches    5
        GroupInnerJoin: 8 items in 5 named groups
        */

 

 

 

그룹 내부 조인 2

var groupJoinQuery3 =
            from category in categories
            join product in products on category.ID equals product.CategoryID into prodGroup
            from prod in prodGroup
            orderby prod.CategoryID
            select new { Category = prod.CategoryID, ProductName = prod.Name };

 

 


 

왼쪽 우선 외부 조인

 

오른쪽 시퀀스에 일치하는 요소가 없는 경우에도 왼쪽 소스 시퀀스의 모든 요소가 반환된다.

왼쪽 우선 외부 조인을 실행하려면 그룹조인과 함께, DefaultIfEmpty 메서드를 사용하여 왼쪽 요소에 일치하는 없을 경우 생성할 기본 오른쪽 요소를 지정

 

       //category.ID equals prod.CategoryID로 일치하는 prod가 없으면 빈 배열이 반환되는데,
       //만약 빈 배열인 경우의 기본값을 지정해주는 것
       var leftOuterQuery =
           from category in categories
           join prod in products on category.ID equals prod.CategoryID into prodGroup
           select prodGroup.DefaultIfEmpty(new Product() { Name = "Nothing!", CategoryID = category.ID });

        // Store the count of total items (for demonstration only).
        int totalItems = 0;

        Console.WriteLine("Left Outer Join:");

        // A nested foreach statement  is required to access group items
        foreach (var prodGrouping in leftOuterQuery)
        {
            Console.WriteLine("Group:");
            foreach (var item in prodGrouping)
            {
                totalItems++;
                Console.WriteLine("  {0,-10}{1}", item.Name, item.CategoryID);
            }
        }

 

 

왼쪽 우선 외부 조인 2

       //DefaultIfEmpty()에 기본값이 없기에 빈 배열일 경우 Null이 반환
       var leftOuterQuery2 =
           from category in categories
           join prod in products on category.ID equals prod.CategoryID into prodGroup
           from item in prodGroup.DefaultIfEmpty()
           select new { Name = item == null ? "Nothing!" : item.Name, CategoryID = category.ID };

        Console.WriteLine("LeftOuterJoin2: {0} items in 1 group", leftOuterQuery2.Count());
        // Store the count of total items
        int totalItems = 0;

        Console.WriteLine("Left Outer Join 2:");

        // Groups have been flattened.
        foreach (var item in leftOuterQuery2)
        {
            totalItems++;
            Console.WriteLine("{0,-10}{1}", item.Name, item.CategoryID);
        }

 

 

 

 


출처

https://learn.microsoft.com/ko-kr/dotnet/csharp/language-reference/keywords/query-keywords

 

Query 키워드 - C# reference

쿼리 키워드 - C# 참조

learn.microsoft.com

 

 

'기타 > C#' 카테고리의 다른 글

LINQ 집합  (0) 2024.07.23
LINQ 집계함수  (0) 2024.07.23
LINQ 메서드 GroupBy  (1) 2024.07.23
LINQ 메서드  (0) 2024.07.23
C# LINQ 소개  (0) 2024.07.21