Úkol kolekce objektů - filtrování
3. Řešení úkolu
Třída Zaměstnanec
namespace kolekce_objektu_filtrovani.Models { public class Zamestnanec { public int Id { get; set; } public string Jmeno { get; set; } = ""; public string Oddeleni { get; set; } = ""; public double Plat { get; set; } } }
Třída Zaměstnanec slouží jako datový model. Definuje vlastnosti Id, Jméno, Oddělení a Plat, které musí mít každý evidovaný pracovník. Instance této třídy budeme ukládat do seznamu (kolekce).
Třída Oddělení
namespace kolekce_objektu_filtrovani.Models { public class Oddeleni { public string NazevOddeleni { get; set; } = ""; public int PocetZamestnancu { get; set; } public double CelkoveNaklady { get; set; } } }
Třída Oddělení slouží jako pomocný datový model pro uložení výsledků našeho seskupování (pomocí metody GroupBy a Select). Definuje vlastnosti Název oddělení, Počet zaměstnanců a Celkové náklady, do kterých budeme ukládat vypočítané souhrnné statistiky k následnému vypsání do tabulky.
Razor komponenta
@page "/" @using kolekce_objektu_filtrovani.Models <PageTitle>Home</PageTitle> <h3>Personální Dashboard - Analýza dat</h3> <div class="row text-center mb-4"> <div class="col-md-3"> <div class="card bg-light p-3"> <h6>Počet zaměstnanců</h6> <h4>@CelkovyPocet</h4> </div> </div> <div class="col-md-3"> <div class="card bg-light p-3"> <h6>Mzdové náklady (Měsíc)</h6> <h4>@CelkoveMzdoveNaklady.ToString("C0")</h4> </div> <!-- C0 udává měnu (C = koruny) a počet desetinných míst (v našem případě 0) --> </div> <div class="col-md-3"> <div class="card bg-light p-3"> <h6>Průměrný plat</h6> <h4>@PrumernyPlat.ToString("C0")</h4> </div> </div> <div class="col-md-3"> <div class="card bg-light p-3"> <h6>Nejvyšší plat</h6> <h4>@NejvyssiPlat.ToString("C0")</h4> </div> </div> </div> <div class="row"> <div class="col-md-6 mb-3"> <div class="card p-3 h-100"> <h5>Nadstandardně placení (Nad 60k)</h5> <p>Seřazeno od nejlépe placeného (Where + OrderBy)</p> <ul> @foreach (var z in DobrePlaceni) { <li>@z.Jmeno - @z.Plat.ToString("C0")</li> } </ul> <hr /> <h5>Pouze jména zaměstnanců (Select)</h5> <p>@string.Join(", ", SeznamJmen)</p> </div> <!-- Join(", "); vypíše všechny položky v kolekci a oddělí je čárkou --> </div> <div class="col-md-6 mb-3"> <div class="card p-3 h-100"> <h5>Náklady podle oddělení (GroupBy + Select)</h5> <table class="table table-sm mt-3"> <thead> <tr> <th>Oddělení</th> <th>Počet lidí</th> <th>Celkové platy</th> </tr> </thead> <tbody> @foreach (var stat in StatistikyPodleOddeleni) { <tr> <td><strong>@stat.NazevOddeleni</strong></td> <td>@stat.PocetZamestnancu</td> <td>@stat.CelkoveNaklady.ToString("C0")</td> </tr> } </tbody> </table> <hr /> <h5>Hledání (FirstOrDefault & Any)</h5> <p> <strong>Zaměstnanec s ID 3:</strong> @(HledanyZamestnanec != null ? HledanyZamestnanec.Jmeno : "Nenalezen") </p> <p> <strong>Máme zaměstnance s platem nad 100k?</strong> @(VysokyPlat ? "Ano" : "Ne") </p> </div> </div> </div> @code { private List<Zamestnanec> zamestnanci = new List<Zamestnanec> // Zdrojová kolekce zaměstnanců { new Zamestnanec { Id = 1, Jmeno = "Jan Novák", Oddeleni = "IT", Plat = 75000 }, new Zamestnanec { Id = 2, Jmeno = "Eva Malá", Oddeleni = "HR", Plat = 45000 }, new Zamestnanec { Id = 3, Jmeno = "Petr Svoboda", Oddeleni = "IT", Plat = 82000 }, new Zamestnanec { Id = 4, Jmeno = "František Dvořák", Oddeleni = "Marketing", Plat = 55000 }, new Zamestnanec { Id = 5, Jmeno = "Karel Šmíd", Oddeleni = "Management", Plat = 120000 } }; private int CelkovyPocet => zamestnanci.Count(); // .Count() spočítá všechny prvky v seznamu private double CelkoveMzdoveNaklady => zamestnanci.Sum(z => z.Plat); // .Sum() projde zaměstnance a sečte jejich platy private double PrumernyPlat => zamestnanci.Average(z => z.Plat); // .Average() vypočítá aritmetický průměr platů private double NejvyssiPlat => zamestnanci.Max(z => z.Plat); // .Max() najde absolutně nejvyšší hodnotu platu private bool VysokyPlat => zamestnanci.Any(z => z.Plat > 100000); // .Any() vrací true, pokud alespoň jeden splní podmínku private Zamestnanec HledanyZamestnanec => zamestnanci.FirstOrDefault(z => z.Id == 3); // .FirstOrDefault() najde prvního s ID 3, jinak vrátí null private List<Zamestnanec> DobrePlaceni => zamestnanci.Where(z => z.Plat >= 60000).OrderByDescending(z => z.Plat).ToList(); // .Where() vyfiltruje data a .OrderByDescending() je seřadí sestupně private List<string> SeznamJmen => zamestnanci.Select(z => z.Jmeno).ToList(); // .Select() extrahuje z objektů pouze vlastnost Jméno (vytvoří seznam textů) private List<Oddeleni> StatistikyPodleOddeleni => zamestnanci .GroupBy(z => z.Oddeleni) // Rozdělí lidi do skupin (IT, HR, Management...) .Select(skupina => new Oddeleni // Z každé skupiny vytvoří jeden statistický objekt { NazevOddeleni = skupina.Key, // .Key obsahuje to, podle čeho se seskupovalo (název) PocetZamestnancu = skupina.Count(), // Spočítá lidi uvnitř dané skupiny CelkoveNaklady = skupina.Sum(z => z.Plat) // Sečte platy pouze lidem v dané skupině }) .ToList(); // Výsledek převede na klasický List }
V části @code se nachází zdrojová kolekce zamestnanci naplněná daty. Agregační metody (např. .Count(), .Sum(), .Average()) provádějí matematické výpočty z celé sady dat a vracejí jedinou hodnotu.
Pro zápis podmínek uvnitř metod používáme tzv. lambda výrazy (např. z => z.Plat). Písmeno z si volíme sami a funguje jako zástupný název pro jednoho konkrétního zaměstnance, kterého příkaz zrovna kontroluje. Šipka => pak programu přesně říká, s jakou vlastností tohoto zaměstnance chceme pracovat.
.Where(z => z.Plat >= 60000) nejprve vybere pouze zaměstnance splňující zadanou podmínku (plat nad 60 000). Na tento zredukovaný výsledek se aplikuje .OrderByDescending(), která data seřadí sestupně. A nakonec metoda .ToList() uloží do klasického seznamu.
.Select(z => z.Jmeno) projde všechny záznamy a vytáhne z nich jen to, co jsme šipkou určili ("z každého zaměstnance z vyber jen jeho jméno"). Místo složitých objektů tak získáme jednoduchý seznam textů (List<string>).
.GroupBy(z => z.Oddeleni) rozdělí lidi do menších skupin podle oddělení. Navazující .Select(skupina => new Oddeleni {...}) pak už nepracuje s jednotlivci, ale vezme rovnou celou vzniklou skupinu a vytvoří z ní jeden nový souhrnný objekt. Přitom se pomocí .Count() a .Sum() rovnou vypočítají statistiky, které se izolovaně aplikují pouze na lidi uvnitř právě zpracovávané skupiny.