Операции над множествами (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
, Type
и OrderFromSun
. Существует несколько экземпляров планет static readonly
типа Planet
. Это удобные определения для хорошо известных планет. Элемент Type
определяет тип планеты.
namespace SolarSystem;
enum PlanetType
{
Rock,
Ice,
Gas,
Liquid
};
Distinct
и DistinctBy
В следующем примере показано поведение метода Enumerable.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
*/
Вместо Distinct
можно использовать DistinctBy
, который принимает 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. Возвращаемая последовательность содержит только те элементы из первой входной последовательности, которых нет во второй.
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
*/
Вместо Except
можно использовать метод ExceptBy, который принимает две последовательности потенциально разнородных типов и keySelector
. Тип 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
записывается в консоль.
Intersect
и IntersectBy
В следующем примере показано поведение 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
*/
Вместо Intersect
можно использовать метод IntersectBy, который принимает две последовательности потенциально разнородных типов и keySelector
. 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
записываются в консоль.
Union
и UnionBy
В следующем примере показана операция объединения двух последовательностей строк. Возвращаемая последовательность содержит уникальные элементы из обеих входных последовательностей.
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
*/
Вместо Union
можно использовать метод UnionBy, который принимает две последовательности одного типа и keySelector
. 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
записываются в консоль.