Две входных однотипных последовательности объединяются методами одна в конец другой как есть, либо с удалением дубликатов:
int[] sequence1 = new[] { 1, 2, 3 },
sequence2 = new[] { 3, 4, 5 };
IEnumerable<int> concat = sequence1.Concat(sequence2),
// 1, 2, 3, 3, 4, 5
union = sequence1.Union(sequence2);
// 1, 2, 3, 4, 5
Могут вычисляться пересечения и исключения:
IEnumerable<int> intersect = sequence1.Intersect(sequence2),
// 3
except1 = sequence1.Except(sequence2),
// 1, 2
except2 = sequence2.Except(sequence1);
// 4, 5
Операции запросов объединены в цепочки:
var sequence = new[] {
"олеся", "аня", "саша", "алёна",
"таня", "дима", "яна", "юра" };
var query = sequence
.Where(n => n.Contains("а"))
.OrderBy(n => n) // bca->abc
.Select(n => n.ToUpper()) // abc->ABC
.ToList();
Console.WriteLine(string.Join(" ", query));
// АЛЁНА АНЯ ДИМА САША ТАНЯ ЮРА ЯНА
Предыдущий запрос в форме выражения запроса:
var sequence = new[] {
"олеся", "аня", "саша", "алёна",
"таня", "дима", "яна", "юра" };
var query =
(from n in sequence
where n.Contains("а")
orderby n // bca->abc
select n.ToUpper()) // abc->ABC
.ToList();
Console.WriteLine(string.Join(" ", query));
// АЛЁНА АНЯ ДИМА САША ТАНЯ ЮРА ЯНА
Использование выражения запросов для внешнего запроса и операции запросов в подзапросе:
var sequence = new[] {
"олеся", "аня", "саша", "алёна",
"таня", "дима", "яна", "юра" };
var query =
(from n in sequence
where n.Contains("а")
// subquery: minimum length
where n.Length == sequence.Min(n2 => n2.Length)
orderby n // bca->abc
select n.ToUpper()) // abc->ABC
.ToList();
Console.WriteLine(string.Join(" ", query));
// АНЯ ЮРА ЯНА
Ключевое слово let
объявляет новую переменную одновременно с переменной
диапазона n
:
var sequence = new[] {
"олеся", "аня", "саша", "алёна",
"таня", "дима", "яна", "юра" };
var query =
(from n in sequence
let subset = Regex.Replace(n, "[аиеёоуыэюя]", "") // without vowels
where subset.Length > 1 // length more 1
orderby n // bca->abc
select n.ToUpper() + "\t" + subset.ToUpper()) // abc->ABC
.ToList();
query.ForEach(n => Console.WriteLine(n));
/* Output:
АЛЁНА ЛН
ДИМА ДМ
ОЛЕСЯ ЛС
САША СШ
ТАНЯ ТН
*/
Чтобы добавить конструкции после завершающих select
или group
применяется
продолжающее слово into
:
var query =
(from c in "Всему своё время. Время разрушать и время строить.".Split(' ', '.')
select c.ToUpper() // select
into upper // 'c' out of context
where upper.StartsWith("В")
select upper) // select
.ToList();
Console.WriteLine(string.Join(" ", query));
// ВСЕМУ ВРЕМЯ ВРЕМЯ ВРЕМЯ
Больше одного генератора from
в запросе:
var numbers = new[] { 1, 2, 3 };
var letters = new[] { 'a', 'b', 'c' };
var query =
(from n in numbers
from l in letters
select n.ToString() + l)
.ToList();
Console.WriteLine(string.Join(" ", query));
// 1a 1b 1c 2a 2b 2c 3a 3b 3c
Векторное произведение фильтруется where
:
var sequence = new[] { "олеся", "аня", "саша", "алёна" };
var query =
(from n1 in sequence
from n2 in sequence
where n1.CompareTo(n2) < 0 // n1 before n2 true
orderby n1, n2 // bca->abc
select n1.ToUpper() + "\t" + n2.ToUpper()) // abc->ABC
.ToList();
query.ForEach(n => Console.WriteLine(n));
/* Output:
АЛЁНА АНЯ
АЛЁНА ОЛЕСЯ
АЛЁНА САША
АНЯ ОЛЕСЯ
АНЯ САША
ОЛЕСЯ САША
*/
Второй генератор from
допускает использование первой переменной диапазона:
var sequence = new[] {
"Анна Каренина", "Признания Мегрэ" };
var query =
(from fullName in sequence
from name in fullName.Split(' ', '.') // fullName
select name.ToUpper() + " из " + fullName.ToUpper()) // abc->ABC
.ToList();
query.ForEach(n => Console.WriteLine(n));
/* Output:
АННА из АННА КАРЕНИНА
КАРЕНИНА из АННА КАРЕНИНА
ПРИЗНАНИЯ из ПРИЗНАНИЯ МЕГРЭ
МЕГРЭ из ПРИЗНАНИЯ МЕГРЭ
*/
Доступно три операции слияния, основные Join
и GroupJoin
выполняются на основе
ключей поиска и обладают высокой производительностью, так как поиск использует
хеш-таблицы. Условие соединения должно использовать операцию эквивалентности.
Join
– плоский результирующий набор.
GroupJoin
– иерархический результирующий набор.
var customers = new[] {
new { ID = 1, Name = "олеся" }, new { ID = 2, Name = "аня" },
new { ID = 3, Name = "саша" } };
var purchases = new[] {
new { CustomerID = 1, Product = "яхта" }, new { CustomerID = 2, Product = "дом" },
new { CustomerID = 2, Product = "самолёт" }, new { CustomerID = 3, Product = "машина" } };
var query =
(from c in customers
join p in purchases
on c.ID equals p.CustomerID
select c.Name.ToUpper() + "\t" + p.Product.ToUpper()) // abc->ABC
.ToList();
query.ForEach(n => Console.WriteLine(n));
/* Output:
ОЛЕСЯ ЯХТА
АНЯ ДОМ
АНЯ САМОЛЁТ
САША МАШИНА
*/
Аналогичное объединение с применением генераторов from
:
var query =
(from c in customers
from p in purchases
where c.ID == p.CustomerID
select c.Name.ToUpper() + "\t" + p.Product.ToUpper()) // abc->ABC
.ToList();
query.ForEach(n => Console.WriteLine(n));
/* Output:
ОЛЕСЯ ЯХТА
АНЯ ДОМ
АНЯ САМОЛЁТ
САША МАШИНА
*/
Выражение запросов GroupJoin
сходно с Join
, но требует добавления после
join-конструкции into
для ввода новой переменной диапазона.
Простейшую операцию слияния представляет Zip
, которая возвращает
последовательность в следствии применения функции к каждой паре элементов:
var sequence1 = new[] { 1, 2, 3 };
var sequence2 = new[] { "один", "два", "три", "пропущен" };
sequence1.Zip(sequence2, (n, w) => n + " " + w)
.ToList().ForEach(nw => Console.WriteLine(nw));
/* Output:
1 один
2 два
3 три
*/
Сортировка последовательности выполняется через orderby
с любым количеством
критериев:
var sequence = new[] {
"олеся", "аня", "саша", "алёна",
"таня", "дима", "яна", "юра" };
var query =
(from n in sequence
orderby n.Length, n // length+bca->abc
select n.ToUpper()) // abc->ABC
.ToList();
Console.WriteLine(string.Join(" ", query));
// АНЯ ЮРА ЯНА ДИМА САША ТАНЯ АЛЁНА ОЛЕСЯ
Инверсия сортировки производится добавлением в orderby
после критерия слова
descending
:
...
orderby n.Length descending, n
...
// АЛЁНА ОЛЕСЯ ДИМА САША ТАНЯ АНЯ ЮРА ЯНА
Реорганизация входной последовательности в последовательность групп через
выражение group
+ by
:
var sequence = new[] {
"олеся", "аня", "саша", "алёна",
"таня", "дима", "яна", "юра" };
var query =
from n in sequence
group n.ToUpper() by n.Length; // abc->ABC+length
query.ToList()
.ForEach(group =>
{
Console.Write($"Length {group.Key}:");
group.ToList()
.ForEach(name => Console.Write(" " + name));
Console.Write("\n");
});
/* Output:
Length 5: ОЛЕСЯ АЛЁНА
Length 3: АНЯ ЯНА ЮРА
Length 4: САША ТАНЯ ДИМА
*/
Для сортировки добавляется после конструкции group продолжение into
с новой
переменной диапазона и оператор orderby
:
var query =
from n in sequence
group n.ToUpper() by n.Length // abc->ABC+length
into grouping
orderby grouping.Key // 231->123
select grouping;
...
/* Output:
Length 3: АНЯ ЯНА ЮРА
Length 4: САША ТАНЯ ДИМА
Length 5: ОЛЕСЯ АЛЁНА
*/
Необобщённые коллекции IEnumerable
приводятся к обобщённой последовательности
IEnumerable<T>
операциями OfType
и Cast
.
Если входной элемент с несовместимым типом:
Cast
– генерирует исключение.OfType
– пропускает.
Тип целевого элемента подставляется непосредственно после генератора from
в
запросе:
...
from int n in sequence...