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í popíšeme, co vše nám LINQ nabízí, tedy metody, přesněji řečeno operátory, které můžeme 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 ta 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 ta, 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 z výsledných prvků můžeme 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 v případě Where()
máme také u operátoru
Select()
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čnou funkcí k Take()
. Vybere tedy
všechny prvky kromě několika prvních, které přeskočí, od čehož se
odvíjí název operátoru.
Vyberme z pole všechna čísla kromě prvních pěti:
{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 totéž čí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é čísla přeskočí,
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 stejně
jako u příkladu s Take()
na dotazu tak, že ho ozávorkujeme.
Toto již nebudeme u dalších metod uvádět.
Řadicí operátory
S OrderBy()
, OrderByDescending()
,
ThenBy()
a ThenByDescending()
jsme se již setkali.
Ukažme si však, 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 dvě
kolekce a na výstupu množina (kolekce) obsahující všechny prvky dvou
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 dvě
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í.