セット操作 (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 積集合 (2 つのコレクションのそれぞれに出現する要素) を返します。 該当なし。 Enumerable.Intersect
Enumerable.IntersectBy
Queryable.Intersect
Queryable.IntersectBy
Union または UnionBy 和集合 (2 つのコレクションのどちらかに出現する一意の要素) を返します。 該当なし。 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 は位置指定レコードであり、そのインスタンスを作成するには引数 NameTypeOrderFromSun が必要です。 Planet 型には、static readonly の複数の惑星インスタンスがあります。 これらは、よく知られた惑星の便利な定義です。 Type メンバーは、惑星の種類を示します。

namespace SolarSystem;

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

Distinct および DistinctBy

次の例は、文字列のシーケンスに対する Enumerable.Distinct メソッドの動作を示しています。 返されたシーケンスには、入力シーケンスからの一意の要素が格納されています。

Graphic showing the behavior of 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 インスタンスが、コンソールに出力されます。

Except および ExceptBy

次の例で、Enumerable.Except の動作を説明します。 返されたシーケンスには、1 つ目の入力シーケンスのうち、2 つ目の入力シーケンスには存在しない要素が格納されています。

Graphic showing the action of 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 メソッドは Except に対する別の方法であり、異なる種類と keySelector の 2 つのシーケンスを受け取ります。 keySelector は 2 番目のコレクション型と同じ型であり、ソースの種類の比較識別子として使用されます。 次のような惑星の配列を考えます。

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

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

最初のコレクションで 2 番目のコレクションに含まれていない惑星を検索するには、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# コードでは:

  • keySelectorstatic ローカル関数として定義されており、惑星の名前で判別します。
  • 最初の惑星配列は、名前に基づいて、2 番目の惑星配列に存在しない惑星にフィルター処理されます。
  • 結果の planet インスタンスが、コンソールに出力されます。

Intersect および IntersectBy

次の例で、Enumerable.Intersect の動作を説明します。 返されたシーケンスには、両方の入力シーケンスに共通する要素が格納されています。

Graphic showing the intersection of two sequences.

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 メソッドは Intersect に対する別の方法であり、異なる種類と keySelector の 2 つのシーケンスを受け取ります。 keySelector は、2 番目のコレクションの種類の比較識別子として使用されます。 次のような惑星の配列を考えます。

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

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

惑星の 2 つの配列があり、1 番目は太陽から見て最初の 5 つの惑星を表し、2 番目は太陽から見て最後の 5 つの惑星を表します。 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# コードでは:

  • 2 つの Planet 配列は、値の比較セマンティクスによって交差されます。
  • 結果のシーケンスには、両方の配列で見つかった惑星のみが表示されます。
  • 結果の planet インスタンスが、コンソールに出力されます。

Union および UnionBy

次の例は、2 つの文字列シーケンスに対する和集合演算を示しています。 返されたシーケンスには、両方の入力シーケンスからの一意の要素が格納されています。

Graphic showing the union of two sequences.

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 メソッドは Union に対する別の方法であり、同じ種類と keySelector の 2 つのシーケンスを受け取ります。 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
};

これら 2 つのコレクションを 1 つのシーケンスに結合するには、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# コードでは:

  • 2 つの Planet 配列は、record 値の比較セマンティクスを使用して結合されます。
  • 結果の planet インスタンスが、コンソールに出力されます。

関連項目