Lekce 9 - LINQ operátory 1
V předchozím kvízu, Kvíz - Slovníky, množiny, fronta, zásobník v C# .NET Kolekce, jsme si ověřili nabyté zkušenosti z předchozích lekcí.
Se základní syntaxí LINQ dotazů jsme již tedy obeznámeni. V několika lekcích si nyní popišme co vše nám LINQ nabízí, tedy metody, přesněji řečeno operátory, které můžete ve svých dotazech používat. Vše si ukážeme na příkladech. Prvních několik příkladů bude opakováním, s dalšími nabude práce s LINQ nových rozměrů.
Restrikční operátory
Výsledek dotazu můžeme nějak podmínit a vybrat tedy jen data, která
splňují nějakou podmínku. Mezi restrikční operátory patří nám již
známé where
.
where
Operátor where
umožňuje vybrat jen ta data, která splňují
určitou podmínku. Z posloupnosti čísel vybereme tak, která jsou větší
než 5
:
{CSHARP_CONSOLE}
int[] cisla = { 3, 5, 8, 5, 9, 1, 3, 4 };
var dotaz = from c in cisla
where (c > 5)
select c;
foreach (int c in dotaz)
Console.WriteLine(c);
{/CSHARP_CONSOLE}
Dotaz vybere:
Konzolová aplikace
8
9
Všechny další příklady budou obsahovat stejný kód pro výpis výsledku, v článku jej již znovu uvádět nebudeme.
Indexované Where()
Co jsme si ještě neukazovali je použití tzv. indexovaného
Where()
, ve kterém můžeme pracovat s indexem prvku v kolekci.
Vyberme čísla, která mají stejnou hodnotu jako jejich index v poli:
{CSHARP_CONSOLE}
int[] cisla = { 0, 5, 2, 5, 4, 1, 3, 7 };
var dotaz = cisla.Where((cislo, index) => cislo == index);
foreach (int c in dotaz)
Console.WriteLine(c);
{/CSHARP_CONSOLE}
Dotaz vybere:
Konzolová aplikace
0
2
4
7
Použili jsme zde mimochodem zápis dotazu přes metody. Některé operátory jinak zapsat nelze a nepodporují SQL-like zápis, budeme se tu s nimi setkávat i nadále.
Projekční operátory
S vybranými prvky se nemusíme spokojit tak, jak jsou, ale můžeme z výsledných prvků vybrat pouze nějakou vlastnost.
select
Pomocí select
určíme co konkrétně nás u vybraných prvků
zajímá. Nechme si vrátit dvojnásobky čísel větších než
5
:
{CSHARP_CONSOLE}
int[] cisla = { 3, 5, 8, 5, 9, 1, 3, 4 };
var dotaz = from c in cisla
where (c > 5)
select c * 2;
foreach (int c in dotaz)
Console.WriteLine(c);
{/CSHARP_CONSOLE}
Dotaz vybere:
Konzolová aplikace
16
18
Stejně tak můžeme mapovat i nějakou vlastnost nebo výsledek metody,
např. Length
nebo ToLower()
na řetězci:
{CSHARP_CONSOLE}
string[] slova = { "SOcialNi", "SiT", "ITnetWOrk" };
var dotaz = from s in slova
select s.ToLower();
foreach (string s in dotaz)
Console.WriteLine(s);
{/CSHARP_CONSOLE}
Dotaz vybere:
Konzolová aplikace
socialni
sit
itnetwork
Indexovaný Select()
s anonymními typy
Stejně jako Where()
i u operátoru Select()
máme
přístup k indexu prvku. S anonymními typy jsme se seznámili v minulé lekci,
ukažme si tedy, jak vybrat anonymní typ, obsahující pozici a hodnotu daného
prvku:
{CSHARP_CONSOLE}
int[] cisla = { 3, 5, 8, 5 };
var dotaz = cisla.Select((cislo, index) => new { Index = index, Hodnota = cislo });
foreach (var d in dotaz)
Console.WriteLine(d);
{/CSHARP_CONSOLE}
Dotaz vybere:
Konzolová aplikace
{ Index = 0, Hodnota = 3 }
{ Index = 1, Hodnota = 5 }
{ Index = 2, Hodnota = 8 }
{ Index = 3, Hodnota = 5 }
Rozdělující operace
Původní kolekci můžeme nějakým způsobem rozdělit a dále pracovat pouze s její částí.
Take()
Take vybere prvních několik prvků z kolekce a zbytek zahodí. Vyberme si
pouze první 3
čísla z pole:
{CSHARP_CONSOLE}
int[] cisla = { 3, 5, 8, 5, 9, 1, 3, 4 };
var dotaz = cisla.Take(3);
foreach (int c in dotaz)
Console.WriteLine(c);
{/CSHARP_CONSOLE}
Dotaz vybere:
Konzolová aplikace
3
5
8
Take()
s dotazem
Take()
můžeme zavolat i na výsledku LINQ dotazu tak, že ho
ozávorkujeme:
{CSHARP_CONSOLE}
int[] cisla = { 3, 5, 8, 5, 9, 1, 3, 4 };
var dotaz = (from c in cisla
where (c > 3)
select c * 2).Take(3);
foreach (int c in dotaz)
Console.WriteLine(c);
{/CSHARP_CONSOLE}
Dotaz vybere:
Konzolová aplikace
10
16
10
Skip()
Skip()
je opačná funkce k Take()
, vybere tedy
všechny prvky kromě několika prvních, které přeskočí, od toho název
operátoru.
Vyberme z pole všechna čísla kromě 5 prvních:
{CSHARP_CONSOLE}
int[] cisla = { 3, 5, 8, 5, 9, 1, 3, 4 };
var dotaz = cisla.Skip(5);
foreach (int c in dotaz)
Console.WriteLine(c);
{/CSHARP_CONSOLE}
Dotaz vybere:
Konzolová aplikace
1
3
4
Pomocí Skip()
a Take()
se často řeší výběr
náhodného prvku:
{CSHARP_CONSOLE}
int[] cisla = { 3, 5, 8, 5, 9, 1, 3, 4 };
Random r = new Random();
var dotaz = cisla.Skip(r.Next(cisla.Length)).Take(1);
foreach (int c in dotaz)
Console.WriteLine(c);
{/CSHARP_CONSOLE}
Dotaz vybere 1
náhodné číslo z pole.
Při spuštění online se výsledek uloží do mezipaměti a bude to vypadat, že padá stále to samé číslo. Obnovení mezipaměti můžete provést změnou zdrojového kódu, např. přidáním nějakého komentáře.
TakeWhile()
Prvky můžeme vybírat postupně od začátku až do splnění určité
podmínky. Od té chvíle přidávání prvků do výsledku ustane. Vyberme si
prvních několik čísel, které jsou větší než 2
:
{CSHARP_CONSOLE}
int[] cisla = { 3, 5, 8, 5, 9, 1, 3, 4 };
var dotaz = cisla.TakeWhile(c => c > 2);
foreach (int c in dotaz)
Console.WriteLine(c);
{/CSHARP_CONSOLE}
Dotaz vybere:
Konzolová aplikace
3
5
8
5
9
TakeWhile()
můžeme také indexovat.
SkipWhile()
Analogicky existuje i SkipWhile()
, které by čísla
přeskakovalo dokud platí určitá podmínka a až poté začne čísla do
výsledku přidávat. Přeskočme prvních několik čísel, která jsou
větší než 2
:
{CSHARP_CONSOLE}
int[] cisla = { 3, 5, 8, 5, 9, 1, 3, 4 };
var dotaz = cisla.SkipWhile(c => c > 2);
foreach (int c in dotaz)
Console.WriteLine(c);
{/CSHARP_CONSOLE}
Dotaz vybere:
Konzolová aplikace
1
3
4
SkipWhile()
můžeme rovněž indexovat.
Skip()
můžeme (jako každou podobnou metodu) zavolat jako u
příkladu s Take()
na dotazu tak, že ho ozávorkujeme. Toto již
nebudu u dalších metod uvádět.
Řadicí operátory
S OrderBy()
, OrderByDescending()
,
ThenBy()
a ThenByDescending()
jsme se již setkali.
Ukažme si ale, jak můžeme řadit pomocí Compareru.
OrderBy()
pomocí
IComparer
Použití comparerů získává svou výhodu ve chvíli, kdy chceme dotaz parametrizovat a střídat kritéria podle kterých třídíme (necháme jejich výběr např. na uživateli). Nejprve je důležité deklarovat si svůj comparer, v ukázce použijeme comparer stringů, který porovnává stringy s ohledem na velká a malá písmena:
public class CaseSensitiveComparer : IComparer<string> { public int Compare(string x, string y) { return string.Compare(x, y, StringComparison.Ordinal); } }
Nyní comparer vložíme do dotazu:
{CSHARP_CONSOLE} string[] slova = { "Argentina", "anakonda", "aLbert", "Bizon", "brčál", "BOmba" }; var dotaz = slova.OrderBy(s => s, new CaseSensitiveComparer()); foreach (string s in dotaz) Console.WriteLine(s); {/CSHARP_CONSOLE}
{CSHARP_OOP} public class CaseSensitiveComparer : IComparer<string> { public int Compare(string x, string y) { return string.Compare(x, y, StringComparison.Ordinal); } } {/CSHARP_OOP}
Dotaz vybere:
Konzolová aplikace
Argentina
BOmba
Bizon
aLbert
anakonda
brčál
Množinové operátory
Na kolekci můžeme nahlížet jako na množinu a použít následující operátory, které nám často zjednoduší práci:
Distinct()
Distinct()
vybere z kolekce pouze unikátní elementy. Vyberme
tedy pouze unikátní čísla z pole:
{CSHARP_CONSOLE}
int[] cisla = { 3, 5, 8, 5, 9, 1, 3, 4 };
var dotaz = cisla.Distinct();
foreach (int c in dotaz)
Console.WriteLine(c);
{/CSHARP_CONSOLE}
Dotaz vybere:
Konzolová aplikace
3
5
8
9
1
4
Union()
Union()
vybere množinové sjednocení. Na vstupu jsou tedy 2
kolekce a na výstupu množina (kolekce) obsahující všechny prvky 2
vstupních kolekcí tak, že je každý obsažen pouze jednou. Zkusme si to:
{CSHARP_CONSOLE}
int[] mnozina1 = { 3, 5, 8, 5, 9, 1, 3, 4 };
int[] mnozina2 = { 3, 7, 2, 1, 4 };
var dotaz = mnozina1.Union(mnozina2);
foreach (int c in dotaz)
Console.WriteLine(c);
{/CSHARP_CONSOLE}
Dotaz vybere:
Konzolová aplikace
3
5
8
9
1
4
7
2
Intersect()
Intersect()
vybere množinový průnik. Na vstupu jsou tedy 2
kolekce a na výstupu množina (kolekce) obsahující pouze prvky které jsou
oběma vstupním kolekcím společné. Zkusme si to:
{CSHARP_CONSOLE}
int[] mnozina1 = { 3, 5, 8, 5, 9, 1, 3, 4 };
int[] mnozina2 = { 3, 7, 2, 1, 4 };
var dotaz = mnozina1.Intersect(mnozina2);
foreach (int c in dotaz)
Console.WriteLine(c);
{/CSHARP_CONSOLE}
Dotaz vybere:
Konzolová aplikace
3
1
4
Except()
Metoda Except()
nám umožňuje vytvořit posloupnost
obsahující ty hodnoty z první množiny, které se nevyskytují v množině
druhé.
{CSHARP_CONSOLE}
int[] mnozina1 = { 3, 5, 8, 5, 9, 1, 3, 4 };
int[] mnozina2 = { 3, 7, 2, 1, 4 };
var dotaz = mnozina1.Except(mnozina2);
foreach (int c in dotaz)
Console.WriteLine(c);
{/CSHARP_CONSOLE}
Dotaz vybere:
Konzolová aplikace
5
8
9
V následujícím cvičení, Řešené úlohy k 7.-9. lekci práce s kolekcemi v C# .NET, si procvičíme nabyté zkušenosti z předchozích lekcí.