cs

Obsługa kolekcji w języku C#, użycie technologii LINQ.

Ogólnie o LINQ

LINQ (Language Integrated Query) to technologia wprowadzona w języku C# i innych językach platformy .NET, umożliwiająca wykonywanie zapytań do różnych źródeł danych (kolekcji, baz danych, XML, JSON itp.) w sposób deklaratywny i zintegrowany z językiem. Poniżej przykłady

JSON

string jsonData = """[
    { "name": "Adam", "active": true },
    { "name": "Ewa", "active": false },
    { "name": "Kamil", "active": true }
]""";
var jsonUsers = JsonSerializer.Deserialize<List<User>>(jsonData);
var activeUsers = from u in jsonUsers
                    where u.Active
                    select u;
Console.WriteLine("Aktywni użytkownicy:");
foreach (var user in activeUsers)
{
    Console.WriteLine(user.Name);
}

Entity framework (symulacja wyników zapytania)

class User
{
    public string Name { get; set; }
    public int Age { get; set; }
    public bool Active { get; set; }
}

List<User> users = new List<User>
{
    new User { Name = "Adam", Age = 25 },
    new User { Name = "Ewa", Age = 17 },
    new User { Name = "Kamil", Age = 30 }
};
var adultUsers = from u in users
                    where u.Age > 18
                    select u;
Console.WriteLine("Dorośli użytkownicy:");
foreach (var user in adultUsers)
{
    Console.WriteLine(user.Name);
}

XML

string xmlData = """
<library>
    <book>
        <title>C# in Depth</title>
        <price>45</price>
    </book>
    <book>
        <title>LINQ Unleashed</title>
        <price>55</price>
    </book>
</library>
""";
XDocument doc = XDocument.Parse(xmlData);
var cheapBooks = from book in doc.Descendants("book")
                    where (int)book.Element("price") < 50
                    select book.Element("title").Value;
Console.WriteLine("Tanie książki:");
foreach (var title in cheapBooks)
{
    Console.WriteLine(title);
}

Kolekecje w języku C#

List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var evenNumbers = from n in numbers
                    where n % 2 == 0
                    select n;
Console.WriteLine("Liczby parzyste:");
foreach (var num in evenNumbers)
{
    Console.WriteLine(num);
}

Składnia

Mamy dwie możliwości zapisu zapytań w technologii LINQ:

List<int> numbers = new List<int> { 1, 2, 6, 8, 3, 10, 12 };

// Query syntax
var result = from n in numbers
             where n % 2 == 0 && n > 5
             orderby n
             select n;

foreach (var n in result)
{
    Console.WriteLine(n);
}

// Method syntax
var result2 = numbers
             .Where(n => n % 2 == 0 && n > 5)
             .OrderBy(n => n);

foreach (var n in result2)
{
    Console.WriteLine(n);
}

Wybrane operacje:

Operacja LINQ query syntax Method syntax
Źródło danych from x in kolekcja kolekcja
Filtrowanie where x > 10 .Where(x => x > 10)
Sortowanie rosnąco orderby x .OrderBy(x => x)
Sortowanie malejąco orderby x descending .OrderByDescending(x => x)
Projekcja (wybór) select x .Select(x => x)
Zgrupowanie group x by x.Category .GroupBy(x => x.Category)
Zgrupowanie z aliasem group x by x.Category into g .GroupBy(...) i operacje na g
Join (łączenie) join y in B on x.Id equals y.Id .Join(B, x => x.Id, y => y.Id, (x, y) => new {...})
Łączenie z lewym zewnętrznym (brak natywnie) .GroupJoin(...).SelectMany(...)
Podzapytanie pomocnicze let wynik = ... .Select(x => { var wynik = ... })
Zliczanie elementów (brak) .Count() / .Count(x => x.Warunek)
Suma wartości (brak) .Sum(x => x.Wartość)
Średnia wartość (brak) .Average(x => x.Wartość)
Największa wartość (brak) .Max(x => x.Wartość)
Najmniejsza wartość (brak) .Min(x => x.Wartość)
Pierwszy element (brak) .First() / .FirstOrDefault()
Ostatni element (brak) .Last() / .LastOrDefault()
Czy istnieje jakiś element (brak) .Any() / .Any(x => x.Warunek)
Czy wszystkie spełniają warunek (brak) .All(x => x.Warunek)
Czy zawiera element (brak) .Contains(element)
Unikalne wartości (brak) .Distinct()
Pominięcie elementów (brak) .Skip(n)
Pobranie pierwszych n (brak) .Take(n)
Konwersja do listy/tablicy (brak) .ToList(), .ToArray()

Wymagania technologii LINQ

var query = list.Where(x => x > 5);
list.Add(10); // zmieniamy kolekcję po stworzeniu zapytania
foreach (var item in query)
{
    Console.WriteLine(item); // wypisze 10, mimo że nie było w zapytaniu
}
var query = list.Where(x => x > 5);
Console.WriteLine(query.Count()); // pierwsza enumeracja
foreach (var item in query) { ... } // druga enumeracja
var result = list.OrderBy(x => x).Take(5); // posortuj całą kolekcję
var result = list.Take(5).OrderBy(x => x); // ogranicz dane, potem sortuj
// Zamiast skomplikowanego zpaytania
var result = list.GroupBy(x => x.Category)
                 .Where(g => g.Key == "A")
                 .SelectMany(g => g); 
// Wykonaj kilka mniejszych
var grouped = list.GroupBy(x => x.Category);
var filtered = grouped.Where(g => g.Key == "A");
var result = filtered.SelectMany(g => g);
var result = list.Where(x => x.Name?.StartsWith("A") == true); // Bez ? jeśli Name jest nullem zapytanie wyrzuci wyjątek NullReferenceException
var customers = context.Customers.ToList();
foreach (var customer in customers)
{
    var orders = context.Orders.Where(o => o.CustomerId == customer.Id).ToList(); // N+1 zapytań
}

var customersWithOrders = context.Customers
                                  .Include(c => c.Orders) // Funkcja dostępna w Entity Framework
                                  .ToList(); // jedno zapytanie

Zadania

Zadanie1

Dana jest lista imion

List<string> names = new List<string> { "Anna", "Kasia", "Bartek", "Andrzej", "Krystyna" };

Wypisz tylko imiona, które zaczynają się na literę „A” oraz mają więcej niż 3 znaki wykorzystując LINQ.

Zadanie 2

Dana jest lista liczb:

List<int> numbers = Enumerable.Range(1, 100).ToList();

Znajdź wszystkie liczby z tej listy, które są podzielne jednocześnie przez 3 i 5 wykorzystując LINQ.

Zadanie 3

Dana jest lista produktów:

class Product {
   public string Name { get; set; }
   public double Price { get; set; }
}
List<Product> products = new List<Product> {
   new Product { Name = "Laptop", Price = 3000 },
   new Product { Name = "Smartphone", Price = 2000 },
   new Product { Name = "Tablet", Price = 1500 }
};

Posortuj produkty malejąco po cenie i wybierz tylko te, które kosztują więcej niż 1500.

Zadanie 4

List<string> names = new List<string> { "Ola", "Tomek", "Basia", "Jan", "Krzysztof", "Ela" };

Pogrupuj imiona według długości (ilości znaków) i wypisz grupy (np. długość 3: Ola, Jan, Ela).

Zadanie 5

class Customer {
   public int Id { get; set; }
   public string Name { get; set; }
}

class Order {
   public int CustomerId { get; set; }
   public string Product { get; set; }
}
List<Customer> customers = new List<Customer> {
   new Customer { Id = 1, Name = "Adam" },
   new Customer { Id = 2, Name = "Ewa" }
};

List<Order> orders = new List<Order> {
   new Order { CustomerId = 1, Product = "Laptop" },
   new Order { CustomerId = 1, Product = "Mouse" },
   new Order { CustomerId = 2, Product = "Tablet" }
};

Wykorzystując LINQ wykonaj zapytanie któro połączy klienta z zamówieniem a następnie wypisz imię klienta z nazwąprzedmiotu który zamawiał.