dev/asp.net, c#

[c#] LINQ 컬렉션 표준 쿼리 메서드 2 - (중복제거,차집합,교집합,합집합,건너뛰기) Concat, Distinct, Except , Intersect, Union, Skip, Take, 객체 중복 제거하기,

코딩for 2022. 11. 11. 16:16
반응형

 

 

집합, 분할, 연결 관련한 컬렉션 메소드 에 대해 알아봅니다.

메서드

메서드 이름 설명 C# 쿼리 식 구문
Distinct 컬렉션에서 중복 값을 제거합니다.  
Except  두 번째 컬렉션에 표시되지 않는 한 컬렉션의 요소를 의미하는 차집합을 반환  
Intersect 두 컬렉션에 각각 표시되는 요소를 의미하는 교집합을 반환합니다.  
Union  두 컬렉션 중 하나에 표시되는 고유한 요소를 의미하는 합집합을 반환합니다.  
Concat 두 시퀀스를 연결하여 하나의 시퀀스를 구성  
Skip 시퀀스에서 지정한 위치까지 요소를 건너뜁니다.  
SkipLast 시퀀스에서 뒤에서 지정한 위치까지 요소를 건너뜁니다.  
SkipWhile 요소가 조건을 충족하지 않을 때까지 조건자 함수를 기반으로 하여 요소를 건너뜁니다.  
Take 시퀀스에서 지정된 위치까지 요소를 사용합니다.  
TakeLast 시퀀스에서 뒤에서 지정된 위치까지 요소를 사용합니다.  
TakeWhile 요소가 조건을 충족하지 않을 때까지 조건자 함수를 기반으로 하여 요소를 사용합니다.  

 

집합

동일 컬렉션이나 별개 컬렉션(또는 집합)에 동등한 요소가 있는지 여부에 따라 결과 집합을 생성

Distinct, Except, Intersect, Union
int[] scores = { 80, 60, 70, 40, 70, 100, 70 };
int[] scores2 = { 70, 100 };

// 중복제거
var distinct = scores.Distinct();

// 차집합
var except = scores.Except(scores2);

// 교집합
var intersect = scores.Intersect(scores2);

// 합집합
var union = scores.Union(scores2);

 

 

사용자 데이터 형식(객체) 중복 제거 방법

* 사용자 지정 데이터 형식의 개체 시퀀스를 비교하려면  클래스에서 IEquatable<T> 제네릭 인터페이스를 구현해야 합니다. 아래는 IEquatable<T> 를 구현한 예제입니다.

IEquatable<T> 상속받아서 Equals, GetHashCode  를 재정의합니다. 

class Student  : IEquatable<Student>
{
    public int grade;       //학년
    public string name;     //이름

    public bool Equals(Student other)
    {
        if (other is null)
            return false;

        return this.grade == other.grade && this.name == other.name;
    }

    public override bool Equals(object obj) => Equals(obj as Student);
    public override int GetHashCode() => (grade, name).GetHashCode();
}
사용예
List<Student> students = new List<Student>
{
    new Student{grade = 3, name = "Tom" },
    new Student{grade = 3, name = "Brian" },
    new Student{grade = 4, name = "Chris" },
    new Student{grade = 5, name = "James" },
    new Student{grade = 5, name = "James" },
};

List<Student> students2 = new List<Student>
{
    new Student{grade = 3, name = "Tom" },
    new Student{grade = 4, name = "Chris" },
    new Student{grade = 6, name = "Erick" },
};

Console.WriteLine("== Distinct ==");
foreach (var student in students.Distinct())
{
    Console.WriteLine(student.grade + ", " + student.name);
}
/*
3, Tom
3, Brian
4, Chris
5, James
*/

Console.WriteLine("== Except ==");
foreach (var student in students.Except(students2))
{
    Console.WriteLine(student.grade + ", " + student.name);
}
/*
3, Brian
5, James
*/

Console.WriteLine("== Intersect ==");
foreach (var student in students.Intersect(students2))
{
    Console.WriteLine(student.grade + ", " + student.name);
}
/*
3, Tom
4, Chris
*/

Console.WriteLine("== Union ==");
foreach (var student in students.Union(students2))
{
    Console.WriteLine(student.grade + ", " + student.name);
}
/*
3, Tom
3, Brian
4, Chris
5, James
6, Erick
*/

예제에서 Except<T>  를 호출하면 James 중복이 제거된 후 응답하는 것처럼

IEquatabl<T> 를 상속받아서 구현된 객체는 비교 연산을 수행하는 메소드 호출시  중복데이터는 제거된다.

 

 

연결

연결은 한 시퀀스를 다른 시퀀스에 추가하는 작업을 나타냅니다.

 

Concat
Console.WriteLine("== Concat ==");
foreach (var student in students.Concat(students2))
{
    Console.WriteLine(student.grade + ", " + student.name);
}
/*
3, Tom
3, Brian
4, Chris
5, James
5, James
3, Tom
4, Chris
6, Erick
*/

* Union 은 집합연산시 중복을 제거하지만 Concat은 두 객체를 연결한다.

 

 

분할

입력 시퀀스를 두 개의 섹션으로 나누는 작업

시퀀스 요소들에서 특정 갯수만 가져오거나 할때 유용하다.

 

Skip, SkipWhile, Take, TakeWhile
List<Student> students = new List<Student>
{
    new Student{grade = 3, name = "Tom" },
    new Student{grade = 3, name = "Brian" },
    new Student{grade = 4, name = "Chris" },
    new Student{grade = 5, name = "James" },
    new Student{grade = 5, name = "James" },
};

Console.WriteLine("== Skip ==");
foreach (var student in students.Skip(3))
{
    Console.WriteLine(student.grade + ", " + student.name);
}
/*
5, James
5, James
*/

Console.WriteLine("== SkipLast ==");
foreach (var student in students.SkipLast(2))
{
    Console.WriteLine(student.grade + ", " + student.name);
}
/*
3, Tom
3, Brain
4, Chris
*/

Console.WriteLine("== SkipWhile ==");
foreach (var student in students.SkipWhile(st => st.grade == 3))    //grade == 3 이면 건너뜀
{
    Console.WriteLine(student.grade + ", " + student.name);
}
/*
4, Chris
5, James
5, James
*/

Console.WriteLine("== Take ==");
foreach (var student in students.Take(3))
{
    Console.WriteLine(student.grade + ", " + student.name);
}
/*
3, Tom
3, Brain
4, Chris
*/

Console.WriteLine("== TakeLast ==");
foreach (var student in students.TakeLast(2))
{
    Console.WriteLine(student.grade + ", " + student.name);
}
/*
5, James
5, James
*/

Console.WriteLine("== TakeWhile ==");
foreach (var student in students.TakeWhile(x=> x.grade==3)) //grade == 3 인 경우만 가져옴
{
    Console.WriteLine(student.grade + ", " + student.name);
}
/*
3, Tom
3, Brain
*/

 

 

참고

https://learn.microsoft.com/ko-kr/dotnet/csharp/programming-guide/concepts/linq/set-operations

https://learn.microsoft.com/ko-kr/dotnet/csharp/programming-guide/concepts/linq/concatenation-operations

반응형