Операции над множествами (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 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
В приведенном выше коде C#:
- Массив Planet фильтруется по первому вхождению каждого уникального типа планеты.
- Полученные экземпляры planet записываются в консоль.
Except и ExceptBy
В следующем примере показано поведение Enumerable.Except. Возвращаемая последовательность содержит только те элементы из первой входной последовательности, которых нет во второй.

string[] planets1 = ["Mercury", "Venus", "Earth", "Jupiter"]; string[] planets2 = ["Mercury", "Earth", "Mars", "Jupiter"]; IEnumerable 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
В приведенном выше коде C#:
- keySelector определяется как локальная функция static , которая различает имена планет.
- Первый массив планет фильтруется, чтобы получить отсутствующие во втором массиве планеты, на основе имен планет.
- Полученный экземпляр planet записывается в консоль.
Intersect и IntersectBy
В следующем примере показано поведение Enumerable.Intersect. Возвращаемая последовательность содержит элементы, общие для обеих входных последовательностей.

string[] planets1 = ["Mercury", "Venus", "Earth", "Jupiter"]; string[] planets2 = ["Mercury", "Earth", "Mars", "Jupiter"]; IEnumerable 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
В приведенном выше коде C#:
- Два массива Planet пересекаются с использованием семантики сравнения их значений.
- В результирующей последовательности содержатся только те планеты, которые обнаружены в обоих массивах.
- Полученные экземпляры planet записываются в консоль.
Union и UnionBy
В следующем примере показана операция объединения двух последовательностей строк. Возвращаемая последовательность содержит уникальные элементы из обеих входных последовательностей.

string[] planets1 = ["Mercury", "Venus", "Earth", "Jupiter"]; string[] planets2 = ["Mercury", "Earth", "Mars", "Jupiter"]; IEnumerable 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
В приведенном выше коде C#:
- Эти два массива Planet объединяются с использованием семантики сравнения значений record .
- Полученные экземпляры planet записываются в консоль.
См. также
- System.Linq
- Общие сведения о стандартных операторах запроса (C#)
- Практическое руководство. Объединение и сравнение коллекций строк (LINQ) (C#)
- Нахождение разности множеств между двумя списками (LINQ) (C#)
Совместная работа с нами на GitHub
Источник этого содержимого можно найти на GitHub, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.
Объединение двух коллекций Laravel
У меня болит голова от работы с коллекциями Laravel. У меня две коллекции:
$dt = Carbon::now(); $days = new Collection([]); /** * Create a calender month */ for ($day = 1; $day daysInMonth; $day++) < $date = Carbon::create($dt->year, $dt->month, $day)->toDateString(); $days->push(new Timesheet([ 'date' => $date, ])); > /** * Get all timesheets for user */ $timesheets = Timesheet::where('user_id', $this->user->id) ->get();
#attributes: array:5 [▼ "id" => "1" "user_id" => "1" "date" => "2016-02-22 22:05:01" "created_at" => "2016-02-22 22:05:01" "updated_at" => "2016-02-22 22:05:01" ] // . one or more .
У меня есть вторая коллекция, дающая мне все дни в течение определенного месяца.
#attributes: array:1 [▼ "date" => "2016-02-01 00:00:00" ] // . and the rest of the month.
Я хочу объединить коллекцию $days с коллекцией $timesheet , сохраняя значения коллекции $timesheet и удаляя любые дубликаты, присутствующие в коллекции $days . E. g. если $timesheets уже содержит ‘2016-02-24’ я не, нужно объединить ‘2016-02-24’ с $days . Как это сделать?
Источник
314
4
2020-12-26 10:25:39
BusterX
Требуется авторизация
Вы должны войти, чтобы оставить комментарий.
Don’t Panic — 2 года назад
Я думаю, что одним из способов сделать это было бы добавить метод к вашей модели Movie , используя hasManyThrough , чтобы получить другие фильмы, связанные с режиссером.
public function relatedMovies() < return $this->hasManyThrough('App\Movie', 'App\Director'); >
Затем отдельно загружать эту связь, а не загружать вложенные отношения.
$movie = Movie::with('directors', 'relatedMovies')->find(1);

Ответить 0

Mike Miller — 2 года назад
Хорошо, продолжайте с этим. Логика должна в значительной степени работать, но obv не имеет доступа к вашему классу расписания.
$days = new Collection([]); //basically the same structure i think $timesheets = new Collection([new Collection(['date'=>'2016-02-23','created_at'=>'2016-02-23 14:12:34']),new Collection(['date'=>'2016-02-28','created_at'=>'2016-02-23 14:15:36'])]); $dt = Carbon::now(); for ($day = 1; $day daysInMonth; $day++) < $date = Carbon::create($dt->year, $dt->month, $day)->format('Y-m-d'); //filter your timesheets and see if there is one for this day $timesheet = $timesheets->filter(function($timesheet) use($date)get('date')==$date;>); if(!$timesheet->isEmpty())< //if there is a timesheet for today then add it to your $days collection $days->push($timesheet); >else< //otherwise just stick in the date $days->push(new Collection([ 'date' => $date, ])); > > //voila! dd($days);

Ответить 0

Jeff Lambert — 2 года назад
Используйте merge :
$collection1 = Model1::all(); $collection2 = Model2::all(); $mergedCollection = $collection1->merge($collection2);
Документация В документации говорится об использовании ее с массивами, но, глядя на подпись метода, она будет принимать смешанные аргументы. Тестирование его на локальную установку проекта Laravel 4 сработало для меня.

Ответить 0

Dwijen — 2 года назад
Я не уверен, почему $merged = $timesheets->merge($days); слил только последний элемент. Может быть, кто-то еще может пролить свет на него. Но пока не будет лучшего решения, вы можете это сделать — $merged = array_merge($timesheets->toArray(), $days->toArray()); Надеюсь, что это поможет.

Ответить 0
Как объединить две коллекции c
LINQ предоставляет несколько методов для работы с коллекциями как с множествами, а именно находить их разность, объединение и пересечение.
Разность последовательностей
С помощью метода Except() можно получить разность двух последовательностей:
string[] soft = < "Microsoft", "Google", "Apple">; string[] hard = < "Apple", "IBM", "Samsung">; // разность последовательностей var result = soft.Except(hard); foreach (string s in result) Console.WriteLine(s);
В данном случае из массива soft убираются все элементы, которые есть в массиве hard . Результатом операции будут два элемента:
Microsoft Google
Пересечение последовательностей
Для получения пересечения последовательностей, то есть общих для обоих наборов элементов, применяется метод Intersect :
string[] soft = < "Microsoft", "Google", "Apple">; string[] hard = < "Apple", "IBM", "Samsung">; // пересечение последовательностей var result = soft.Intersect(hard); foreach (string s in result) Console.WriteLine(s);
Так как оба набора имеют только один общий элемент, то соответственно только он и попадет в результирующую выборку:
Apple
Удаление дубликатов
Для удаления дублей в наборе используется метод Distinct :
string[] soft = < "Microsoft", "Google", "Apple", "Microsoft", "Google" >; // удаление дублей var result = soft.Distinct(); foreach (string s in result) Console.WriteLine(s);
Microsoft Google Apple
Объединение последовательностей
Для объединения двух последовательностей используется метод Union . Его результатом является новый набор, в котором имеются элементы, как из первой, так и из второй последовательности. Повторяющиеся элементы добавляются в результат только один раз:
string[] soft = < "Microsoft", "Google", "Apple">; string[] hard = < "Apple", "IBM", "Samsung">; // объединение последовательностей var result = soft.Union(hard); foreach (string s in result) Console.WriteLine(s);
Результатом операции будет следующий набор:
Microsoft Google Apple IBM Samsung
Если же нам нужно простое объединение двух наборов, то мы можем использовать метод Concat :
var result = soft.Concat(hard);
В этом случае те элементы, которые встречаются в обоих наборах, дублируются в резутирующей последовательности.
Последовательное применение методов Concat и Distinct будет подобно действию метода Union.
Работа со сложными объектами
Для сравнения объектов в последовательностях применяются реализации методов GetHeshCode() и Equals(). Поэтому если мы хотим работать с последовательностями, которые содержат объекты своих классов и структур, то нам необходимо определить для них подобные методы:
Person[] students = < new Person("Tom"), new Person("Bob"), new Person("Sam") >; Person[] employees = < new Person("Tom"), new Person("Bob"), new Person("Mike") >; // объединение последовательностей var people = students.Union(employees); foreach (Person person in people) Console.WriteLine(person.Name); class Person < public string Name < get;>public Person(string name) => Name = name; public override bool Equals(object? obj) < if (obj is Person person) return Name == person.Name; return false; >public override int GetHashCode() => Name.GetHashCode(); >
Здесь объекты Person сравниваются исходя из значения их свойства Name — если имена равны, то и объекты Person равны. Консольный вывод:
Как объединить не сколько коллекций в одну?
Приветствую всех! Сегодня хочу показать пример как с помощь LINQ, объединить коллекцию в одну. По условию задания у нас дана коллекция, главный метод и метод который мы должны дописать:
public class Classroom
public List < string >Students = new List < string >( ) ;
public static void Main ( )
Classroom [ ] classes =
new Classroom < Students = < "Pavel" , "Ivan" , "Petr" >, > ,
new Classroom < Students = < "Anna" , "Ilya" , "Vladimir" >, > ,
new Classroom < Students = < "Bulat" , "Alex" , "Galina" >, >
var allStudents = GetAllStudents ( classes ) ;
Array . Sort ( allStudents ) ;
Console . WriteLine ( string . Join ( » » , allStudents ) ) ;
public static string [ ] GetAllStudents ( Classroom [ ] classes )
Как видно в метод GetAllStudents принимает коллекцию коллекций. А вернуть нам нужно массив string[] Который содержит имена всех студентов.
Решение этой тривиальной задачи будет в одну строчку кода, которую мы решим с помощью Linq:
public static string [ ] GetAllStudents ( Classroom [ ] classes )
return classes . SelectMany ( s = > s . Students ) . ToArray ( ) ;
Для решения этой задачи воспользуемся методом SelectMany, почитать можете в чем разница между Select и SelectMany тут всю коллекцию преобразуем в массив string() с помощью метода ToArray();