집합 작업(C#)

LINQ의 집합 작업은 동일 컬렉션이나 별개 컬렉션(또는 집합)에 동등한 요소가 있는지 여부에 따라 결과 집합을 생성하는 쿼리 작업을 가리킵니다.

다음 섹션에는 집합 작업을 수행하는 표준 쿼리 연산자 메서드가 나와 있습니다.

메서드

메서드 이름 설명 C# 쿼리 식 구문 자세한 정보
Distinct 또는 DistinctBy 컬렉션에서 중복 값을 제거합니다. 해당 사항 없음. Enumerable.Distinct
Enumerable.DistinctBy
Queryable.Distinct
Queryable.DistinctBy
Except 또는 ExceptBy 두 번째 컬렉션에 표시되지 않는 한 컬렉션의 요소를 의미하는 차집합을 반환합니다. 해당 사항 없음. Enumerable.Except
Enumerable.ExceptBy
Queryable.Except
Queryable.ExceptBy
Intersect 또는 IntersectBy 두 컬렉션에 각각 표시되는 요소를 의미하는 교집합을 반환합니다. 해당 사항 없음. Enumerable.Intersect
Enumerable.IntersectBy
Queryable.Intersect
Queryable.IntersectBy
Union 또는 UnionBy 두 컬렉션 중 하나에 표시되는 고유한 요소를 의미하는 합집합을 반환합니다. 해당 사항 없음. Enumerable.Union
Enumerable.UnionBy
Queryable.Union
Queryable.UnionBy

예제

다음 예제 중 일부는 태양계의 행성을 나타내는 record 형식을 사용합니다.

namespace SolarSystem;

record Planet(
    string Name,
    PlanetType Type,
    int OrderFromSun)
{
    public static readonly Planet Mercury =
        new(nameof(Mercury), PlanetType.Rock, 1);

    public static readonly Planet Venus =
        new(nameof(Venus), PlanetType.Rock, 2);

    public static readonly Planet Earth =
        new(nameof(Earth), PlanetType.Rock, 3);

    public static readonly Planet Mars =
        new(nameof(Mars), PlanetType.Rock, 4);

    public static readonly Planet Jupiter =
        new(nameof(Jupiter), PlanetType.Gas, 5);

    public static readonly Planet Saturn =
        new(nameof(Saturn), PlanetType.Gas, 6);

    public static readonly Planet Uranus =
        new(nameof(Uranus), PlanetType.Liquid, 7);

    public static readonly Planet Neptune =
        new(nameof(Neptune), PlanetType.Liquid, 8);

    // Yes, I know... not technically a planet anymore
    public static readonly Planet Pluto =
        new(nameof(Pluto), PlanetType.Ice, 9);
}

record Planet은 위치 레코드로, 이를 인스턴스화하려면 Name, TypeOrderFromSun 인수가 필요합니다. Planet 형식에는 여러 static readonly 행성 인스턴스가 있습니다. 이들은 잘 알려진 행성에 대한 편의에 따른 정의입니다. Type 멤버는 행성 유형을 식별합니다.

namespace SolarSystem;

enum PlanetType
{
    Rock,
    Ice,
    Gas,
    Liquid
};

DistinctDistinctBy

다음 예제에서는 문자열 시퀀스에 대한 Enumerable.Distinct 메서드의 동작을 보여 줍니다. 반환된 시퀀스에는 입력 시퀀스의 고유한 요소가 포함됩니다.

Distinct()의 동작을 보여 주는 그래픽

string[] planets = { "Mercury", "Venus", "Venus", "Earth", "Mars", "Earth" };

IEnumerable<string> query = from planet in planets.Distinct()
                            select planet;

foreach (var str in query)
{
    Console.WriteLine(str);
}

/* This code produces the following output:
 *
 * Mercury
 * Venus
 * Earth
 * Mars
 */

DistinctByDistinct에 대한 대체 방법으로, keySelector를 사용합니다. keySelector는 원본 형식의 비교 판별자로 사용됩니다. 다음 행성 배열을 생각해 보겠습니다.

Planet[] planets =
{
    Planet.Mercury,
    Planet.Venus,
    Planet.Earth,
    Planet.Mars,
    Planet.Jupiter,
    Planet.Saturn,
    Planet.Uranus,
    Planet.Neptune,
    Planet.Pluto
};

다음 코드에서 행성은 PlanetType에 따라 구분되며, 각 형식의 첫 번째 행성이 표시됩니다.

foreach (Planet planet in planets.DistinctBy(p => p.Type))
{
    Console.WriteLine(planet);
}

// This code produces the following output:
//     Planet { Name = Mercury, Type = Rock, OrderFromSun = 1 }
//     Planet { Name = Jupiter, Type = Gas, OrderFromSun = 5 }
//     Planet { Name = Uranus, Type = Liquid, OrderFromSun = 7 }
//     Planet { Name = Pluto, Type = Ice, OrderFromSun = 9 }

위의 C# 코드에서:

  • Planet 배열은 각각 고유한 행성 유형의 첫 번째 항목으로 개별적으로 필터링됩니다.
  • 결과 planet 인스턴스는 콘솔에 기록됩니다.

ExceptExceptBy

다음 예제에서는 Enumerable.Except의 동작을 보여줍니다. 반환된 시퀀스에는 두 번째 입력 시퀀스에 없는 첫 번째 입력 시퀀스의 요소만 포함됩니다.

Except()의 동작을 보여 주는 그래픽

string[] planets1 = { "Mercury", "Venus", "Earth", "Jupiter" };
string[] planets2 = { "Mercury", "Earth", "Mars", "Jupiter" };

IEnumerable<string> query = from planet in planets1.Except(planets2)
                            select planet;

foreach (var str in query)
{
    Console.WriteLine(str);
}

/* This code produces the following output:
 *
 * Venus
 */

ExceptBy 메서드는 이종 형식의 두 시퀀스와 keySelector를 사용하는 Except에 대한 대체 접근 방식입니다. keySelector는 두 번째 컬렉션 형식과 형식이 동일하며 원본 형식의 비교 판별자로 사용됩니다. 다음 행성 배열을 생각해 보겠습니다.

Planet[] planets =
{
    Planet.Mercury,
    Planet.Venus,
    Planet.Earth,
    Planet.Jupiter
};

Planet[] morePlanets =
{
    Planet.Mercury,
    Planet.Earth,
    Planet.Mars,
    Planet.Jupiter
};

첫 번째 컬렉션에서 두 번째 컬렉션에 없는 행성을 찾으려면 행성 이름을 second 컬렉션으로 프로젝션하고 동일한 keySelector를 제공하면 됩니다.

// A shared "keySelector"
static string PlanetNameSelector(Planet planet) => planet.Name;

foreach (Planet planet in
    planets.ExceptBy(
        morePlanets.Select(PlanetNameSelector), PlanetNameSelector))
{
    Console.WriteLine(planet);
}

// This code produces the following output:
//     Planet { Name = Venus, Type = Rock, OrderFromSun = 2 }

위의 C# 코드에서:

  • keySelector는 행성 이름을 구분하는 static 로컬 함수로 정의됩니다.
  • 첫 번째 행성 배열은 이름을 기준으로 두 번째 행성 배열에 없는 행성으로 필터링됩니다.
  • 결과 planet 인스턴스는 콘솔에 기록됩니다.

IntersectIntersectBy

다음 예제에서는 Enumerable.Intersect의 동작을 보여줍니다. 반환된 시퀀스에는 입력 시퀀스 둘 다에 공통적으로 있는 요소가 포함됩니다.

두 시퀀스의 교집합을 보여주는 그래픽

string[] planets1 = { "Mercury", "Venus", "Earth", "Jupiter" };
string[] planets2 = { "Mercury", "Earth", "Mars", "Jupiter" };

IEnumerable<string> query = from planet in planets1.Intersect(planets2)
                            select planet;

foreach (var str in query)
{
    Console.WriteLine(str);
}

/* This code produces the following output:
 *
 * Mercury
 * Earth
 * Jupiter
 */

IntersectBy 메서드는 이종 형식의 두 시퀀스와 keySelector를 사용하는 Intersect에 대한 대체 접근 방식입니다. keySelector는 두 번째 컬렉션 형식의 비교 판별자로 사용됩니다. 다음 행성 배열을 생각해 보겠습니다.

Planet[] firstFivePlanetsFromTheSun =
{
    Planet.Mercury,
    Planet.Venus,
    Planet.Earth,
    Planet.Mars,
    Planet.Jupiter
};

Planet[] lastFivePlanetsFromTheSun =
{
    Planet.Mars,
    Planet.Jupiter,
    Planet.Saturn,
    Planet.Uranus,
    Planet.Neptune
};

두 개의 행성 배열이 있습니다. 하나는 태양으로부터 첫 다섯 개 행성을 나타내고, 다른 하나는 태양으로부터의 마지막 다섯 개 행성을 나타냅니다. Planet 형식이 위치 record 형식이므로 keySelector의 형식으로 해당 값 비교 의미 체계를 사용할 수 있습니다.

foreach (Planet planet in
    firstFivePlanetsFromTheSun.IntersectBy(
        lastFivePlanetsFromTheSun, planet => planet))
{
    Console.WriteLine(planet);
}

// This code produces the following output:
//     Planet { Name = Mars, Type = Rock, OrderFromSun = 4 }
//     Planet { Name = Jupiter, Type = Gas, OrderFromSun = 5 }

위의 C# 코드에서:

  • Planet 배열은 해당 값 비교 의미 체계에서 교차됩니다.
  • 두 배열 모두에서 발견된 행성만 결과 시퀀스에 존재합니다.
  • 결과 planet 인스턴스는 콘솔에 기록됩니다.

UnionUnionBy

다음 예제에서는 두 개의 문자열 시퀀스에 대한 합집합을 보여줍니다. 반환된 시퀀스에는 두 입력 시퀀스의 고유한 요소가 모두 포함됩니다.

두 시퀀스의 결합을 보여주는 그래픽

string[] planets1 = { "Mercury", "Venus", "Earth", "Jupiter" };
string[] planets2 = { "Mercury", "Earth", "Mars", "Jupiter" };

IEnumerable<string> query = from planet in planets1.Union(planets2)
                            select planet;

foreach (var str in query)
{
    Console.WriteLine(str);
}

/* This code produces the following output:
 *
 * Mercury
 * Venus
 * Earth
 * Jupiter
 * Mars
 */

UnionBy 메서드는 동일한 형식의 두 시퀀스와 keySelector를 사용하는 Union에 대한 대체 접근 방식입니다. keySelector는 원본 형식의 비교 판별자로 사용됩니다. 다음 행성 배열을 생각해 보겠습니다.

Planet[] firstFivePlanetsFromTheSun =
{
    Planet.Mercury,
    Planet.Venus,
    Planet.Earth,
    Planet.Mars,
    Planet.Jupiter
};

Planet[] lastFivePlanetsFromTheSun =
{
    Planet.Mars,
    Planet.Jupiter,
    Planet.Saturn,
    Planet.Uranus,
    Planet.Neptune
};

이러한 두 컬렉션을 단일 시퀀스로 통합하려면 keySelector를 제공합니다.

foreach (Planet planet in
    firstFivePlanetsFromTheSun.UnionBy(
        lastFivePlanetsFromTheSun, planet => planet))
{
    Console.WriteLine(planet);
}

// This code produces the following output:
//     Planet { Name = Mercury, Type = Rock, OrderFromSun = 1 }
//     Planet { Name = Venus, Type = Rock, OrderFromSun = 2 }
//     Planet { Name = Earth, Type = Rock, OrderFromSun = 3 }
//     Planet { Name = Mars, Type = Rock, OrderFromSun = 4 }
//     Planet { Name = Jupiter, Type = Gas, OrderFromSun = 5 }
//     Planet { Name = Saturn, Type = Gas, OrderFromSun = 6 }
//     Planet { Name = Uranus, Type = Liquid, OrderFromSun = 7 }
//     Planet { Name = Neptune, Type = Liquid, OrderFromSun = 8 }

위의 C# 코드에서:

  • Planet 배열은 해당 record 값 비교 의미 체계를 사용하여 함께 위브됩니다.
  • 결과 planet 인스턴스는 콘솔에 기록됩니다.

참조