Операции над множествами (C#)
Операции задания в LINQ ссылаются на операции запроса, которые создают результирующий набор на основе присутствия или отсутствия эквивалентных элементов в одной или отдельных коллекциях.
Внимание
В этих примерах используется System.Collections.Generic.IEnumerable<T> источник данных. Источники данных, основанные на System.Linq.IQueryProvider использовании System.Linq.IQueryable<T> источников данных и деревьев выражений. Деревья выражений имеют ограничения на допустимый синтаксис C#. Кроме того, каждый IQueryProvider
источник данных, например EF Core , может наложить больше ограничений. Ознакомьтесь с документацией по источнику данных.
Имена методов | Description | Синтаксис выражения запроса 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 |
Distinct
и DistinctBy
.
В следующем примере показано поведение метода Enumerable.Distinct применительно к последовательности строк. Возвращаемая последовательность содержит уникальные элементы из входной последовательности.
string[] words = ["the", "quick", "brown", "fox", "jumped", "over", "the", "lazy", "dog"];
IEnumerable<string> query = from word in words.Distinct()
select word;
foreach (var str in query)
{
Console.WriteLine(str);
}
/* This code produces the following output:
*
* the
* quick
* brown
* fox
* jumped
* over
* lazy
* dog
*/
Вместо Distinct
можно использовать DistinctBy
, который принимает keySelector
. keySelector
используется как сравнительный дискриминатор для типа источника. В следующем коде слова дискриминируются на основе их Length
, а первое слово каждой длины отображается:
string[] words = ["the", "quick", "brown", "fox", "jumped", "over", "the", "lazy", "dog"];
foreach (string word in words.DistinctBy(p => p.Length))
{
Console.WriteLine(word);
}
// This code produces the following output:
// the
// quick
// jumped
// over
Except
и ExceptBy
.
В следующем примере показано поведение Enumerable.Except. Возвращаемая последовательность содержит только элементы из первой входной последовательности, которая не входит во вторую входную последовательность.
Примечание.
В следующих примерах в этой статье используются общие источники данных для этой области.
Каждый из них Student
имеет уровень оценки, основной отдел и ряд показателей. У него Teacher
также есть свойство, определяющее City
кампус, где учитель проводит классы. У Department
него есть имя и ссылка на Teacher
того, кто выступает в качестве руководителя отдела.
Пример набора данных можно найти в исходном репозитории.
public enum GradeLevel
{
FirstYear = 1,
SecondYear,
ThirdYear,
FourthYear
};
public class Student
{
public required string FirstName { get; init; }
public required string LastName { get; init; }
public required int ID { get; init; }
public required GradeLevel Year { get; init; }
public required List<int> Scores { get; init; }
public required int DepartmentID { get; init; }
}
public class Teacher
{
public required string First { get; init; }
public required string Last { get; init; }
public required int ID { get; init; }
public required string City { get; init; }
}
public class Department
{
public required string Name { get; init; }
public int ID { get; init; }
public required int TeacherID { get; init; }
}
string[] words1 = ["the", "quick", "brown", "fox"];
string[] words2 = ["jumped", "over", "the", "lazy", "dog"];
IEnumerable<string> query = from word in words1.Except(words2)
select word;
foreach (var str in query)
{
Console.WriteLine(str);
}
/* This code produces the following output:
*
* quick
* brown
* fox
*/
Вместо Except
можно использовать метод ExceptBy, который принимает две последовательности потенциально разнородных типов и keySelector
. Это keySelector
тот же тип, что и тип первой коллекции. Рассмотрим следующие Teacher
идентификаторы массива и преподавателей, чтобы исключить их. Чтобы найти преподавателей в первой коллекции, которая не находится во второй коллекции, можно проецировать идентификатор преподавателя на вторую коллекцию:
int[] teachersToExclude =
[
901, // English
965, // Mathematics
932, // Engineering
945, // Economics
987, // Physics
901 // Chemistry
];
foreach (Teacher teacher in
teachers.ExceptBy(
teachersToExclude, teacher => teacher.ID))
{
Console.WriteLine($"{teacher.First} {teacher.Last}");
}
В приведенном выше коде C#:
- Массив
teachers
фильтруется только тем учителям, которые не в массивеteachersToExclude
. - Массив
teachersToExclude
содержитID
значение для всех руководителей отделов. - Вызов приводит к
ExceptBy
новому набору значений, записанных в консоль.
Новый набор значений имеет тип, который является типом Teacher
первой коллекции. Каждый teacher
из массивов, не имеющих соответствующего значения идентификатора в teachers
массиве, записывается в teachersToExclude
консоль.
Intersect
и IntersectBy
.
В следующем примере показано поведение Enumerable.Intersect. Возвращаемая последовательность содержит элементы, общие для обеих входных последовательностей.
string[] words1 = ["the", "quick", "brown", "fox"];
string[] words2 = ["jumped", "over", "the", "lazy", "dog"];
IEnumerable<string> query = from word in words1.Intersect(words2)
select word;
foreach (var str in query)
{
Console.WriteLine(str);
}
/* This code produces the following output:
*
* the
*/
Вместо Intersect
можно использовать метод IntersectBy, который принимает две последовательности потенциально разнородных типов и keySelector
. keySelector
используется как сравнительный дискриминатор для типа второй коллекции. Рассмотрим следующие массивы учащихся и преподавателей. Запрос соответствует элементам в каждой последовательности по имени, чтобы найти тех учащихся, которые также являются преподавателями:
foreach (Student person in
students.IntersectBy(
teachers.Select(t => (t.First, t.Last)), s => (s.FirstName, s.LastName)))
{
Console.WriteLine($"{person.FirstName} {person.LastName}");
}
В приведенном выше коде C#:
- Запрос создает пересечение
Teacher
имен иStudent
сравнивает их. - В результирующей последовательности присутствуют только люди, найденные в обоих массивах.
- Полученные экземпляры
Student
записываются в консоль.
Union
и UnionBy
.
В следующем примере показана операция объединения двух последовательностей строк. Возвращаемая последовательность содержит уникальные элементы из обеих входных последовательностей.
string[] words1 = ["the", "quick", "brown", "fox"];
string[] words2 = ["jumped", "over", "the", "lazy", "dog"];
IEnumerable<string> query = from word in words1.Union(words2)
select word;
foreach (var str in query)
{
Console.WriteLine(str);
}
/* This code produces the following output:
*
* the
* quick
* brown
* fox
* jumped
* over
* lazy
* dog
*/
Вместо Union
можно использовать метод UnionBy, который принимает две последовательности одного типа и keySelector
. keySelector
используется как сравнительный дискриминатор для типа источника. Следующий запрос создает список всех людей, которые являются учащимися или преподавателями. Учащиеся, которые также являются учителями, добавляются в набор профсоюзов только один раз:
foreach (var person in
students.Select(s => (s.FirstName, s.LastName)).UnionBy(
teachers.Select(t => (FirstName: t.First, LastName: t.Last)), s => (s.FirstName, s.LastName)))
{
Console.WriteLine($"{person.FirstName} {person.LastName}");
}
В приведенном выше коде C#:
students
Массивыteachers
объединяются вместе с именами в качестве селектора ключей.- Полученные имена записываются в консоль.