Set operations (C#)
Set operations in LINQ refer to query operations that produce a result set that is based on the presence or absence of equivalent elements within the same or separate collections (or sets).
The standard query operator methods that perform set operations are listed in the following section.
Methods
Method names | Description | C# query expression syntax | More information |
---|---|---|---|
Distinct or DistinctBy | Removes duplicate values from a collection. | Not applicable. | Enumerable.Distinct Enumerable.DistinctBy Queryable.Distinct Queryable.DistinctBy |
Except or ExceptBy | Returns the set difference, which means the elements of one collection that do not appear in a second collection. | Not applicable. | Enumerable.Except Enumerable.ExceptBy Queryable.Except Queryable.ExceptBy |
Intersect or IntersectBy | Returns the set intersection, which means elements that appear in each of two collections. | Not applicable. | Enumerable.Intersect Enumerable.IntersectBy Queryable.Intersect Queryable.IntersectBy |
Union or UnionBy | Returns the set union, which means unique elements that appear in either of two collections. | Not applicable. | Enumerable.Union Enumerable.UnionBy Queryable.Union Queryable.UnionBy |
Examples
Some of the following examples rely on a record
type that represents the planets in our solar system.
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);
}
The record Planet
is a positional record, which requires a Name
, Type
, and OrderFromSun
arguments to instantiate it. There are several static readonly
planet instances on the Planet
type. These are convenience-based definitions for well-known planets. The Type
member identifies the planet type.
namespace SolarSystem;
enum PlanetType
{
Rock,
Ice,
Gas,
Liquid
};
Distinct
and DistinctBy
The following example depicts the behavior of the Enumerable.Distinct method on a sequence of strings. The returned sequence contains the unique elements from the input sequence.
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
*/
The DistinctBy
is an alternative approach to Distinct
that takes a keySelector
. The keySelector
is used as the comparative discriminator of the source type. Consider the following planet array:
Planet[] planets =
{
Planet.Mercury,
Planet.Venus,
Planet.Earth,
Planet.Mars,
Planet.Jupiter,
Planet.Saturn,
Planet.Uranus,
Planet.Neptune,
Planet.Pluto
};
In the following code, planets are discriminated based on their PlanetType
, and the first planet of each type is displayed:
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 }
In the preceding C# code:
- The
Planet
array is filtered distinctly to the first occurrence of each unique planet type. - The resulting
planet
instances are written to the console.
Except
and ExceptBy
The following example depicts the behavior of Enumerable.Except. The returned sequence contains only the elements from the first input sequence that are not in the second input sequence.
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
*/
The ExceptBy method is an alternative approach to Except
that takes two sequences of possibly heterogenous types and a keySelector
. The keySelector
is the same type as the second collection's type, and it is used as the comparative discriminator of the source type. Consider the following planet arrays:
Planet[] planets =
{
Planet.Mercury,
Planet.Venus,
Planet.Earth,
Planet.Jupiter
};
Planet[] morePlanets =
{
Planet.Mercury,
Planet.Earth,
Planet.Mars,
Planet.Jupiter
};
To find planets in the first collection that aren't in the second collection, you can project the planet names as the second
collection and provide the same 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 }
In the preceding C# code:
- The
keySelector
is defined as astatic
local function that discriminates on a planet name. - The first planet array is filtered to planets that are not found in the second planet array, based on their name.
- The resulting
planet
instance is written to the console.
Intersect
and IntersectBy
The following example depicts the behavior of Enumerable.Intersect. The returned sequence contains the elements that are common to both of the input 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
*/
The IntersectBy method is an alternative approach to Intersect
that takes two sequences of possibly heterogenous types and a keySelector
. The keySelector
is used as the comparative discriminator of the second collection's type. Consider the following planet arrays:
Planet[] firstFivePlanetsFromTheSun =
{
Planet.Mercury,
Planet.Venus,
Planet.Earth,
Planet.Mars,
Planet.Jupiter
};
Planet[] lastFivePlanetsFromTheSun =
{
Planet.Mars,
Planet.Jupiter,
Planet.Saturn,
Planet.Uranus,
Planet.Neptune
};
There are two arrays of planets; one represents the first five planets from the sun and the second represents the last five planets from the sun. Since the Planet
type is a positional record
type, you can use its value comparison semantics in the form of the 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 }
In the preceding C# code:
- The two
Planet
arrays are intersected by their value comparison semantics. - Only planets that are found in both arrays are present in the resulting sequence.
- The resulting
planet
instances are written to the console.
Union
and UnionBy
The following example depicts a union operation on two sequences of strings. The returned sequence contains the unique elements from both input 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
*/
The UnionBy method is an alternative approach to Union
that takes two sequences of the same type and a keySelector
. The keySelector
is used as the comparative discriminator of the source type. Consider the following planet arrays:
Planet[] firstFivePlanetsFromTheSun =
{
Planet.Mercury,
Planet.Venus,
Planet.Earth,
Planet.Mars,
Planet.Jupiter
};
Planet[] lastFivePlanetsFromTheSun =
{
Planet.Mars,
Planet.Jupiter,
Planet.Saturn,
Planet.Uranus,
Planet.Neptune
};
To union these two collections into a single sequence, you provide the 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 }
In the preceding C# code:
- The two
Planet
arrays are weaved together using theirrecord
value comparison semantics. - The resulting
planet
instances are written to the console.
See also
Feedback
Submit and view feedback for