[C#] LINQ

Date:     Updated:

카테고리:

태그:

C#의 강력한 기능중 하나인 LINQ 입니다. C#의 LINQ는 foreach … if 문으로 IEnumerable타입을 순회할 때 작업하는 코드를 획기적으로 줄여줄 수 있습니다.

LINQ는 쿼리식의 형태와, 람다식의 형태 두가지로 존재 합니다. 이것을 쓰면 리스트에서 어떤 조건에 해당하는 녀석들을 끄집어 낸다던지 하는 작업을 편하게 할 수 있습니다.

본 예제는 C#책에 있는 예제를 그대로 사용하였습니다.

예시

키가 175 이상인 친구들을 가려내는 프로그램


Profile[] arrProfile =
{
    new Profile(){ Name = "정우성", Height = 186},
    new Profile(){ Name = "김태희", Height = 158},
    new Profile(){ Name = "고현정", Height = 172},
    new Profile(){ Name = "이문세", Height = 178},
    new Profile(){ Name = "하동훈", Height = 171}
};
 
List<Profile> profiles = new List<Profile>();
foreach(Profile profile in arrProfile)
{
    if(profile.Height < 175)
    {
        profiles.Add(profile);
    }
}

//키의 오름차순으로 정렬
profiles.Sort(
    (profile1, profile2) =>
    {
        return profile1.Height - profile2.Height;
    });

foreach (var profile in profiles)
{
    Console.WriteLine("{0}, {1}", profile.Name, profile.Height);
}

결과

LinQ를 쓰면 코드를 더 간단하게 만들 수 있음

Profile[] arrProfile =
{
    new Profile(){ Name = "정우성", Height = 186},
    new Profile(){ Name = "김태희", Height = 158},
    new Profile(){ Name = "고현정", Height = 172},
    new Profile(){ Name = "이문세", Height = 178},
    new Profile(){ Name = "하동훈", Height = 171}
};

//LinQ 구문
var profiles = from profile in arrProfile 
               where profile.Height < 175 
               orderby profile.Height 
               select profile;

foreach(var profile in profiles)
{
    Console.WriteLine("{0},{1}", profile.Name, profile.Height);
}

LinQ 사용법

from

  • 모든 쿼리식은 반드시 from절로 시작함
  • 원본과 원본안에 들어가있는 요소 데이터를 변수로 지정
  • foreach의 괄호안에 들어가는 것과 비슷함(범위변수가 차이, foreach는 변수에 데이터를 저장 x, LINQ의 범위변수에는 저장 )
  • [[IEnumerable]] 객체만 사용 가능 (변수 할당 시에)

    where

  • 필터 역할
  • 조건에 부합하는 데이터만 선별함

orderby

  • 데이터의 정렬을 수행하는 연산자
  • 데이터베이스처럼 ASEC 가 있음 - > ascending 물론 반대는 decending

select

  • 최종 결과를 추출하는 쿼리식의 마침표 같은 존재
  • [[IEnumerable]]의 T는 select 문에 의해서 결정됨
  • 무명형식도 가능
Profile[] arrProfile =
{
    new Profile(){ Name = "정우성", Height = 186},
    new Profile(){ Name = "김태희", Height = 158},
    new Profile(){ Name = "고현정", Height = 172},
    new Profile(){ Name = "이문세", Height = 178},
    new Profile(){ Name = "하동훈", Height = 171}
};

var profiles = from profile in arrProfile 
               where profile.Height < 175 
               orderby profile.Height 
               select new { Name = profile.Name, InchHeight = profile.Height * 0.393};

foreach (var profile in profiles)
{
    Console.WriteLine("{0},{1}", profile.Name, profile.InchHeight);
}

여러 개의 데이터 원본에 질의하기

  • 그 as 해서 다시 가공하는 느낌임
  • 처음 가공된 정보를 가지고 한번더 가공함
    class MainApp
    {
      static void Main(string[] args)
      {
          Class[] arrClasses =
          {
                  new Class(){name = "연두반", score = new int[]{ 99,80,70,24} },
                  new Class(){name = "분홍반", score = new int[]{ 60,45,87,72} },
                  new Class(){name = "파랑반", score = new int[]{ 92,30,85,94} },
                  new Class(){name = "노랑반", score = new int[]{ 90,88,0,17} }
              };
    
          var classes = from c in arrClasses
                        from s in c.score
                        where s < 60
                        orderby s
                        select new { c.name, Lowest = s };
    
          foreach(var c in classes)
          {
              Console.WriteLine($"낙제 : {c.name} ({c.Lowest})");
          }
    
      }
    }
    

group by

  • 데이터를 분류하는 문구
  • 분류기준으로 데이터를 분류
  • Key로 그룹을 나눈 기준의 값을 찾아옴
    Profile[] arrProfile =
    {
        new Profile(){ Name = "정우성", Height = 186},
        new Profile(){ Name = "김태희", Height = 158},
        new Profile(){ Name = "고현정", Height = 172},
        new Profile(){ Name = "이문세", Height = 178},
        new Profile(){ Name = "하동훈", Height = 171}
    };
var listProfile = from profile in arrProfile
                  group profile by profile.Height < 175 into g
                  select new { GroupKey = g.Key, Profiles = g };

foreach(var Group in listProfile)
{
    Console.WriteLine($"- 175 미만? : {Group.GroupKey}");
    foreach(var profile in Group.Profiles)
    {
        Console.WriteLine($">>> {profile.Name}, {profile.Height}");
    }
}

Join

  • 두개의 데이터 원본을 연결하는 연산
  • 특정 필드의 값을 비교하여 일치하는 데이터끼리 연결을 수행
  • 내부조인과 외부조인이 있음
  • 내부조인은 하나를 기준으로 두개의 테이블을 합치는 개념이고, 외부조인은 기본적으로 내부조인과 비슷하지만 조인 결과에 기준이 되는 데이터 원본이 모두 포함된다는 점이 다름
class MainApp
{

    class Profile
    {
        public string Name { get; set; }
        public int Height { get; set; }

    }
    class Product
    {
        public string Title { get; set; }
        public string Star { get; set; }
    }

 static void Main(string[] args)
 {
   Profile[] arrProfile =
  {
      new Profile(){ Name = "정우성", Height = 186},
      new Profile(){ Name = "김태희", Height = 158},
      new Profile(){ Name = "고현정", Height = 172},
      new Profile(){ Name = "이문세", Height = 178},
      new Profile(){ Name = "하동훈", Height = 171}
  };

  Product[] arrProduct =
  {
      new Product(){Title = "비트", Star = "정우성"},
      new Product(){Title = "CF 다수", Star = "김태희"},
      new Product(){Title = "아이리스", Star = "김태희"},
      new Product(){Title = "모래시계", Star = "고현정"},
      new Product(){Title = "Solo 예찬", Star = "이문세"}
  };

  var listProfile = from profile in arrProfile
                    join product in arrProduct on profile.Name equals product.Star
                    select new
                    {
                        Name = profile.Name,
                        Work = product.Title,
                        Height = profile.Height
                    };

  Console.WriteLine("--- 내부 조인 결과 ---");
  foreach (var profile in listProfile)
  {
      Console.WriteLine($"이름{profile.Name}, 작품{profile.Work}, 키{profile.Height}");
  }

  listProfile = from profile in arrProfile
                join product in arrProduct on profile.Name equals product.Star into ps
                from product in ps.DefaultIfEmpty(new Product() { Title = "그런거 없음" })
                select new
                {
                    Name = profile.Name,
                    Work = product.Title,
                    Height = profile.Height
                };

  Console.WriteLine("");
  Console.WriteLine("--- 외부 조인 결과 ---");
  foreach(var profile in listProfile)
  {
      Console.WriteLine($"이름{profile.Name}, 작품{profile.Work}, 키{profile.Height}");
  }
 }

}

Linq의 함수형 출력

  • Linq는 사실 쿼리처럼 보이지만 컴파일러 단에서 함수로 바꾸어서 컴파일함
  • 아래가 그 예시임
  • 53개의 표준 LinQ 연산 메소드 중에 C#의 쿼리식에서 지원하는 것은 11개 뿐임
  • 물론 11개로 대부분의 데이터 처리가 가능하지만, 나머지 42개를 모두 활용할 수 있다면 삶이 좀더 편해짐
var profiles = arrProfile.Where(profile => profile.Height < 175)
               .OrderBy(profile => profile.Height)
               .Select(profile => new
{
    Name = profile.Name, InchHeight = profile.Height * 0.393
});


 Console.WriteLine("");
 Console.WriteLine("--- Linq 함수형 출력 ---");
 foreach (var profile in listProfile)
 {
     Console.WriteLine($"이름{profile.Name}, 작품{profile.Work}, 키{profile.Height}");
 }

근데 쿼리식만 적으면 함수들을 못씀 그래서 섞어서 씀

var profiles = from profile in arrProfile
               where profile.Height < 180
               select profile;

double Average = profiles.Average(profile => profile.Height);

Console.WriteLine(Average);

C Sharp 카테고리 내 다른 글 보러가기

댓글 남기기