finish lecture 13, add css
parent
327c710e51
commit
cc779adf08
File diff suppressed because one or more lines are too long
@ -1,528 +1,460 @@
|
|||||||
# Generics, IEnumerable and LINQ
|
---
|
||||||
|
marp: true
|
||||||

|
paginate: true
|
||||||
|
math: mathjax
|
||||||
|
theme: buutti
|
||||||
|
title: N. Generics, IEnumerable and LINQ
|
||||||
---
|
---
|
||||||
|
|
||||||
# Generics
|
# Generics, IEnumerable and LINQ
|
||||||
|
|
||||||
When using lists, you have to define the type of data that will be stored in the list, inside the angled brackets:
|
|
||||||
|
|
||||||
List<int> numberList = new List<int>(); // This list stores variables of type int
|
|
||||||
|
|
||||||
This means that list is a __generic__ class: it can contain data of any type
|
<!-- headingDivider: 5 -->
|
||||||
|
<!-- class: invert -->
|
||||||
|
|
||||||
Classes, structs, interfaces and methods can also be generic
|
## Generics
|
||||||
|
|
||||||
All the variables that are defined generic inside a generic container will be assigned a type only when the containing object/method is called
|
* When using lists, you have to define the type of data that will be stored in the list, inside the angled brackets:
|
||||||
|
```csharp
|
||||||
|
List<int> numberList = new List<int>(); // This list stores variables of type int
|
||||||
|
```
|
||||||
|
* This means that list is a *__generic__* class: it can contain data of any type
|
||||||
|
* Classes, structs, interfaces and methods can also be generic
|
||||||
|
* All the variables that are defined generic inside a generic container will be assigned a type only when the containing object/method is called
|
||||||
|
|
||||||
# Generics - Creating a Generic Class
|
### Creating a generic class
|
||||||
|
|
||||||
|
```csharp
|
||||||
class GenericClassExample<T>
|
class GenericClassExample<T>
|
||||||
|
|
||||||
{
|
{
|
||||||
|
public T value; // This value will be whatever type is specified at instantiation
|
||||||
public T value; // This value will be whatever type is specified at instantiation
|
public void PrintTypeAndValue()
|
||||||
|
{
|
||||||
public void PrintTypeAndValue()
|
Console.WriteLine
|
||||||
|
($"This class contains a variable of type {value.GetType()} and of value {value}");
|
||||||
{
|
}
|
||||||
|
|
||||||
Console.WriteLine
|
|
||||||
|
|
||||||
($"This class contains a variable of type {value.GetType()} and of value {value}");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Program
|
class Program
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
static void Main(string[] args)
|
|
||||||
|
|
||||||
{
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
GenericClassExample<int> example = new GenericClassExample<int>();
|
{
|
||||||
|
GenericClassExample<int> example = new GenericClassExample<int>();
|
||||||
example.value = 20;
|
example.value = 20;
|
||||||
|
example.PrintTypeAndValue();
|
||||||
example.PrintTypeAndValue();
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
```
|
||||||
}
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
# Generics - Multiple Type Parameters
|
### Multiple type parameters
|
||||||
|
|
||||||
Generic classes can receive multiple types as parameters
|
Generic classes can receive multiple types as parameters:
|
||||||
|
|
||||||
|
```csharp
|
||||||
class CustomContainer<T1, T2, T3>
|
class CustomContainer<T1, T2, T3>
|
||||||
|
|
||||||
{
|
{
|
||||||
|
public T1 First { get; set; }
|
||||||
public T1 First { get; set; }
|
public T2 Second { get; set; }
|
||||||
|
public T3 Third { get; set; }
|
||||||
public T2 Second { get; set; }
|
|
||||||
|
|
||||||
public T3 Third { get; set; }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Program
|
class Program
|
||||||
|
|
||||||
{
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
static void Main(string[] args)
|
{
|
||||||
|
CustomContainer<int, string, DateTime> container
|
||||||
{
|
= new CustomContainer<int, string, DateTime>();
|
||||||
|
container.First = 10;
|
||||||
CustomContainer<int, string, DateTime> container
|
container.Second = "Testing.";
|
||||||
|
container.Third = DateTime.Now;
|
||||||
= new CustomContainer<int, string, DateTime>();
|
}
|
||||||
|
|
||||||
container.First = 10;
|
|
||||||
|
|
||||||
container.Second = "Testing.";
|
|
||||||
|
|
||||||
container.Third = DateTime.Now;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
}
|
### Creating a generic Method
|
||||||
|
|
||||||
# Generics - Creating a Generic Method
|
```csharp
|
||||||
|
|
||||||
void GenericMethodExample<T>(T value)
|
void GenericMethodExample<T>(T value)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
Console.WriteLine
|
||||||
Console.WriteLine
|
($"This method was passed a variable of type {value.GetType()} and of value {value}.");
|
||||||
|
|
||||||
($"This method was passed a variable of type {value.GetType()} and of value {value}.");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GenericMethodExample<string>("ABC");
|
GenericMethodExample<string>("ABC");
|
||||||
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
❕
|
* ***Note:*** You could name the generic type as anything, e.g. `<GenericType>`. It is named `<T>` by convention.
|
||||||
|
|
||||||
You could name the generic type as anything, e.g. \<GenericType>. It is named \<T> by convention.
|
|
||||||
|
|
||||||
# Exercise 1: Initializing a Populated List
|
## Exercise 1: Initializing a populated list
|
||||||
|
<!-- _backgroundColor: #29366f -->
|
||||||
|
|
||||||
Create a generic method GetPopulatedList\<T> which takes two parameters: T value and int length, and returns a new list of type T which is populated with the value variables and has a length of length.
|
Create a generic method `GetPopulatedList<T>` which takes two parameters: `T value` and `int length`, and returns a new list of type `T` which is populated with the `value` variables and has a length of `length`.
|
||||||
|
|
||||||
Test your method out with a couple of different types and lengths:
|
Test your method out with a couple of different types and lengths:
|
||||||
|
|
||||||
|
```csharp
|
||||||
List<string> list = GetPopulatedList<string>("Hello, there", 10);
|
List<string> list = GetPopulatedList<string>("Hello, there", 10);
|
||||||
|
|
||||||
foreach(string value in list)
|
foreach(string value in list)
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
Console.WriteLine(value);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# IEnumerable
|
|
||||||
|
|
||||||
Lists and arrays are both __collections __ that implement the __IEnumerable __ interface
|
|
||||||
|
|
||||||
All objects that implement the IEnumerable interface can be iterated with the foreach statement
|
|
||||||
|
|
||||||
IEnumerable<string> names = new string[] {"Harry", "Luke", "Harley"};
|
|
||||||
|
|
||||||
IEnumerable<string> days = new List<string> {"Sunday", "Monday", "Friday"};
|
|
||||||
|
|
||||||
foreach (string name in names)
|
|
||||||
|
|
||||||
Console.WriteLine(name);
|
|
||||||
|
|
||||||
foreach (string day in days)
|
|
||||||
|
|
||||||
Console.WriteLine(day);
|
|
||||||
|
|
||||||
# IEnumerable (continued)
|
|
||||||
|
|
||||||
The IEnumerable interface itself doesn't hold much functionality
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
However, the LINQ library includes all the methods you would typically need to apply to IEnumerables, such as filtering
|
|
||||||
|
|
||||||
# LINQ
|
|
||||||
|
|
||||||
Some common query expressions occur repeatedly in code
|
|
||||||
|
|
||||||
E.g. trying to find an object with a certain id from an array:
|
|
||||||
|
|
||||||
IEnumerable<User> userArray = new User[2] // Initialize a new array of users
|
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
new User { Id = 0, Name = "Rene" } ,
|
|
||||||
|
|
||||||
new User { Id = 1, Name = "Ville" }
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
User FindObjectWithId(int id)
|
|
||||||
|
|
||||||
{
|
{
|
||||||
|
Console.WriteLine(value);
|
||||||
foreach (User user in userArray)
|
|
||||||
|
|
||||||
if (user.Id == id)
|
|
||||||
|
|
||||||
return user;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Console.WriteLine(FindObjectWithId(1).Name); // Outputs "Ville"
|
## IEnumerable
|
||||||
|
|
||||||
# LINQ (continued)
|
* Lists and arrays are both *__collections__* that implement the `IEnumerable` interface
|
||||||
|
* All objects that implement the IEnumerable interface can be iterated with the foreach statement
|
||||||
|
```csharp
|
||||||
|
IEnumerable<string> names = new string[] {"Harry", "Luke", "Harley"};
|
||||||
|
IEnumerable<string> days = new List<string> {"Sunday", "Monday", "Friday"};
|
||||||
|
|
||||||
* Having to write your own method for each possible query operation (select, filter, sort…) would of course be nonsensical
|
foreach (string name in names)
|
||||||
* The List class includes some methods for manipulation, but...
|
Console.WriteLine(name);
|
||||||
* These only work on lists; not all IEnumerables (e.g. arrays) contain those methods
|
|
||||||
* Add to this all the different types of data (objects, SQL databases, XML, JSON…)
|
foreach (string day in days)
|
||||||
|
Console.WriteLine(day);
|
||||||
|
```
|
||||||
|
|
||||||
To introduce extensive query capabilities to all collection types, Language-Integrated Query (LINQ) was created
|
---
|
||||||
|
|
||||||
LINQ supports querying of objects and even XML and SQL data, directly in your code
|
|
||||||
|
|
||||||
Get started by adding the namespace to your project:
|
|
||||||
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
Here's the first example using LINQ:
|
|
||||||
|
|
||||||
IEnumerable<User> userArray = new User[2] // Initialize a new array of users
|
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
new User { Id = 0, Name = "Rene" } ,
|
|
||||||
|
|
||||||
new User { Id = 1, Name = "Ville"}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
Console.WriteLine(userArray.First(user => user.Id == 1).Name); // Outputs "Ville"
|
|
||||||
|
|
||||||
The __First __ -method returns the first result that satisfies the expression in the parameters
|
|
||||||
|
|
||||||
The arrow syntax above is called a __lambda expression__
|
|
||||||
|
|
||||||
# Lambda Expressions
|
|
||||||
|
|
||||||
Lambda expressions are a quick way of writing one line methods
|
|
||||||
|
|
||||||
The "First" -method of LINQ takes a __delegate __ as a parameter
|
|
||||||
|
|
||||||
The following code would do exactly the same thing as the example in the previous slide:
|
* The IEnumerable interface itself doesn't hold much functionality:
|
||||||
|

|
||||||
|
* However, the LINQ library includes all the methods you would typically need to apply to IEnumerables, such as filtering
|
||||||
|
|
||||||
// Parameter references an existing method
|
## LINQ
|
||||||
|
|
||||||
bool GetUserWithId(User user)
|
### The problem
|
||||||
|
|
||||||
{
|
* Here's an example of a common *__query__* a programmer might have to do:
|
||||||
|
* Try to find an object with a certain id from an array
|
||||||
|
|
||||||
return user.Id == 1;
|
```csharp
|
||||||
|
IEnumerable<User> userArray = new User[2] // Initialize a new array of users
|
||||||
|
{
|
||||||
|
new User { Id = 0, Name = "Rene" } ,
|
||||||
|
new User { Id = 1, Name = "Ville" }
|
||||||
|
};
|
||||||
|
|
||||||
}
|
User FindObjectWithId(int id)
|
||||||
|
{
|
||||||
|
foreach (User user in userArray)
|
||||||
|
if (user.Id == id)
|
||||||
|
return user;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Console.WriteLine(FindObjectWithId(1).Name); // Outputs "Ville"
|
||||||
|
```
|
||||||
|
|
||||||
Console.WriteLine(userList.First(GetUserWithId).Name); // Outputs "Ville"
|
---
|
||||||
|
|
||||||
As would the following:
|
* Having to write your own method for every possible query operation (select, filter, sort…) would be nonsensical
|
||||||
|
* The `List` class includes some methods for manipulation, but...
|
||||||
|
* These only work on lists; not all `IEnumerables` (e.g. arrays) contain those methods!
|
||||||
|
* Not to mention different data types altogether (objects, SQL databases, XML, JSON…)
|
||||||
|
* What to do?
|
||||||
|
|
||||||
// The delegate is created inside the parameters as an anonymous method
|
### The solution
|
||||||
|
|
||||||
Console.WriteLine(userList.First(delegate (User user)
|
* To introduce extensive query capabilities to all collection types, Language-Integrated Query (LINQ) was created
|
||||||
|
* LINQ supports querying of objects and even XML and SQL data, directly in your code
|
||||||
|
* Get started by adding the [System.Linq](https://learn.microsoft.com/en-us/dotnet/api/system.linq?view=net-9.0) namespace to your project:
|
||||||
|
```csharp
|
||||||
|
using System.Linq;
|
||||||
|
```
|
||||||
|
|
||||||
{ return user.Id == 1; })); // Outputs "Ville"
|
---
|
||||||
|
|
||||||
# Lambda Expressions (continued)
|
* Here's a solution to the earlier example that uses LINQ:
|
||||||
|
```csharp
|
||||||
|
IEnumerable<User> userArray = new User[2] // Initialize a new array of users
|
||||||
|
{
|
||||||
|
new User { Id = 0, Name = "Rene" } ,
|
||||||
|
new User { Id = 1, Name = "Ville"}
|
||||||
|
};
|
||||||
|
Console.WriteLine(userArray.First(user => user.Id == 1).Name); // Outputs "Ville"
|
||||||
|
```
|
||||||
|
* The [Enumerable.First](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.first?view=net-9.0) method returns the first result that satisfies the expression in the parameters
|
||||||
|
|
||||||
|
* The arrow syntax above is called a *__lambda expression__*
|
||||||
|
|
||||||
|
## Lambda expressions
|
||||||
|
|
||||||
|
* Lambda expressions are a quick way of writing one-line methods
|
||||||
|
* The `First` method of LINQ takes a [delegate](11.%20Delegates%20and%20Events.md#delegates) as a parameter
|
||||||
|
* We could just declare a method beforehand, and then refer to it in `First` like this:
|
||||||
|
```csharp
|
||||||
|
bool GetUserWithId(User user)
|
||||||
|
{
|
||||||
|
return user.Id == 1;
|
||||||
|
}
|
||||||
|
Console.WriteLine(userList.First(GetUserWithId).Name); // Outputs "Ville"
|
||||||
|
```
|
||||||
|
* To shorten the expression, we could use a delegate inside the parameters to refer to an anonymous method:
|
||||||
|
```csharp
|
||||||
|
Console.WriteLine(userList.First(delegate (User user)
|
||||||
|
{ return user.Id == 1; })); // Outputs "Ville"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lambda expressions compared
|
||||||
|
|
||||||
Using LINQs "First" -query with...
|
Using LINQs "First" -query with...
|
||||||
|
<div class='columns111' markdown='1'>
|
||||||
|
<div markdown='1'>
|
||||||
|
|
||||||
...a method:
|
...a method:
|
||||||
|
```csharp
|
||||||
bool GetUserWithId(User user)
|
bool GetUserWithId(User user)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
return user.Id == 1;
|
||||||
return user.Id == 1;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
|
userList.First(
|
||||||
userList.First(
|
GetUserWithId).Name);
|
||||||
|
|
||||||
GetUserWithId).Name);
|
|
||||||
|
|
||||||
// Outputs "Ville"
|
// Outputs "Ville"
|
||||||
|
```
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div markdown='1'>
|
||||||
|
|
||||||
...an anonymous method:
|
...an anonymous method:
|
||||||
|
|
||||||
|
```csharp
|
||||||
Console.WriteLine(userList.First(
|
Console.WriteLine(userList.First(
|
||||||
|
|
||||||
delegate (User user)
|
delegate (User user)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
return user.Id == 1;
|
||||||
return user.Id == 1;
|
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Outputs "Ville"
|
// Outputs "Ville"
|
||||||
|
```
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div markdown='1'>
|
||||||
|
|
||||||
...a lambda expression:
|
...a lambda expression:
|
||||||
|
|
||||||
|
```csharp
|
||||||
Console.WriteLine(userList.First(
|
Console.WriteLine(userList.First(
|
||||||
|
user =>
|
||||||
user =>
|
user.Id == 1).Name);
|
||||||
|
|
||||||
user.Id == 1).Name);
|
|
||||||
|
|
||||||
// Outputs "Ville"
|
// Outputs "Ville"
|
||||||
|
```
|
||||||
|
|
||||||
# Lambda Expressions - Example
|
</div>
|
||||||
|
</div>
|
||||||
Normal methods can also be declared using the arrow function
|
|
||||||
|
|
||||||
static void Main(string[] args)
|
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
// Method body assigned with lambda expression
|
|
||||||
|
|
||||||
string PrintCheckUpper(bool upper, string text) => upper ? text.ToUpper() : text;
|
|
||||||
|
|
||||||
PrintCheckUpper(true, "I'm not angry!"); // Outputs I'M NOT ANGRY!
|
### Lambda expressions: An example
|
||||||
|
|
||||||
}
|
Regular methods can also be declared using the arrow function
|
||||||
|
|
||||||
# LINQ and Lambda Expressions
|
|
||||||
|
|
||||||
Going back to our LINQ example, it could be written without lambda expressions:
|
|
||||||
|
|
||||||
bool GetUserWithId(User user)
|
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
|
// Method body assigned with lambda expression
|
||||||
return user.Id == 1;
|
string PrintCheckUpper(bool upper, string text) =>
|
||||||
|
upper ? text.ToUpper() : text;
|
||||||
|
|
||||||
|
PrintCheckUpper(true, "I'm not angry!"); // Outputs I'M NOT ANGRY!
|
||||||
|
PrintCheckUpper(false, "I'm not angry!"); // Outputs I'm not angry!
|
||||||
}
|
}
|
||||||
|
```
|
||||||
IEnumerable<User> userArray = new User[2] // Initialize a new array of users
|
|
||||||
|
### LINQ and Lambda expressions
|
||||||
{
|
|
||||||
|
* Going back to our LINQ example...
|
||||||
new User { Id = 0, Name = "Rene" } ,
|
```csharp
|
||||||
|
IEnumerable<User> userArray = new User[2] // Initialize a new array of users
|
||||||
new User { Id = 1, Name = "Ville"}
|
{
|
||||||
|
new User { Id = 0, Name = "Rene" } ,
|
||||||
};
|
new User { Id = 1, Name = "Ville"}
|
||||||
|
};
|
||||||
Console.WriteLine(userArray.First(GetUserWithId).Name); // Outputs "Ville"
|
```
|
||||||
|
* Here's a solution without lambda expressions:
|
||||||
Here's the shorter version with lambda expression again:
|
```csharp
|
||||||
|
bool GetUserWithId(User user)
|
||||||
Console.WriteLine(userArray.First(user => user.Id == 1).Name); // Outputs "Ville"
|
{
|
||||||
|
return user.Id == 1;
|
||||||
# LINQ Methods
|
}
|
||||||
|
Console.WriteLine(userArray.First(GetUserWithId).Name); // Outputs "Ville"
|
||||||
LINQ contains methods for filtering, ordering, grouping, joining and selecting
|
```
|
||||||
|
* And here's the shorter version with lambda expression:
|
||||||
Suppose we have a class Person that contains a property Country
|
```csharp
|
||||||
|
Console.WriteLine(userArray.First(user => user.Id == 1).Name); // Outputs "Ville"
|
||||||
The following performs filtering to persons object, returning only the persons whose country is of value "Finland":
|
```
|
||||||
|
|
||||||
List<Person> persons = new List<Person> {/* Insert data here */};
|
### LINQ methods
|
||||||
|
|
||||||
var queryResult = persons
|
* LINQ contains methods for filtering, ordering, grouping, joining and selecting
|
||||||
|
* Suppose we have a class `Person` that contains a property `Country`
|
||||||
.Where(person => person.Country == "Finland");
|
* The following performs filtering to the `persons` object, returning only the persons whose country is of value "Finland":
|
||||||
|
```csharp
|
||||||
# LINQ Methods (continued)
|
List<Person> persons = new List<Person> {/* Insert data here */};
|
||||||
|
|
||||||
| Method | Example | Description |
|
var queryResult = persons
|
||||||
| :-: | :-: | :-: |
|
.Where(person => person.Country == "Finland");
|
||||||
| Where | persons.Where(p => p.Country == "Finland") | Filters results based on an expression |
|
```
|
||||||
| OrderBy | persons.OrderBy(p => p.LastName) | Orders results based on one of its properties |
|
|
||||||
| Select | persons.Select(p => $"Dr. {p.LastName}") | Converts the enumerable into an another type based on an expression |
|
### LINQ methods listed
|
||||||
| Skip | persons.Skip(1) | Skips first N elements |
|
|
||||||
| Take | persons.Take(5) | Returns N elements |
|
| Method | Example | Description |
|
||||||
| ToList | persons.ToList() | Converts IEnumerable to a list |
|
|:--------|:---------------------------------------------|:--------------------------------------------------------------------|
|
||||||
| ToArray | persons.ToArray() | Converts IEnumerable to an array |
|
| Where | `persons.Where(p => p.Country == "Finland")` | Filters results based on an expression |
|
||||||
|
| OrderBy | `persons.OrderBy(p => p.LastName)` | Orders results based on one of its properties |
|
||||||
|
| Select | `persons.Select(p => $"Dr. {p.LastName}")` | Converts the enumerable into an another type based on an expression |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Tässä on kaikki aritmeettiset operaattorit
|
| Method | Example | Description |
|
||||||
|
|:--------|:--------------------|:-----------------------------------|
|
||||||
| Method | Example | Description |
|
| Skip | `persons.Skip(1)` | Skips first N elements |
|
||||||
| :-: | :-: | :-: |
|
| Take | `persons.Take(5)` | Returns N elements |
|
||||||
| Any | persons.Any(p => p.Country == "Finland") | Return true if at least one element satisfies a condition |
|
| ToList | `persons.ToList()` | Converts `IEnumerable` to a list |
|
||||||
| All | persons.All(p => p.Age >= 18) | Return true if all elements satisfy a condition |
|
| ToArray | `persons.ToArray()` | Converts `IEnumerable` to an array |
|
||||||
| FirstOrDefault | persons.FirstOrDefault(p => !p.Active) | Returns the first element that satisfies a condition, or null if not found |
|
|
||||||
| Count | persons.Count(p => p.FirstName == "Mauri") | Returns the count of elements that satisfy a condition, can be left blank to count all |
|
|
||||||
|
|
||||||
More LINQ methods can be found [here](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable?view=net-7.0)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Tässä on kaikki aritmeettiset operaattorit
|
| Method | Example | Description |
|
||||||
|
|:---------------|:---------------------------------------------|:---------------------------------------------------------------------------------------|
|
||||||
# LINQ - Example
|
| Any | `persons.Any(p => p.Country == "Finland")` | Return `true` if at least one element satisfies a condition |
|
||||||
|
| All | `persons.All(p => p.Age >= 18)` | Return `true` if all elements satisfy a condition |
|
||||||
Suppose we have a csv file authors.csv in our project directory
|
| FirstOrDefault | `persons.FirstOrDefault(p => !p.Active)` | Returns the first element that satisfies a condition, or `null` if not found |
|
||||||
|
| Count | `persons.Count(p => p.FirstName == "Mauri")` | Returns the count of elements that satisfy a condition, can be left blank to count all |
|
||||||
// using System.IO;
|
|
||||||
|
|
||||||
string path = @"C:\\some\\path\\authors.csv";
|
|
||||||
|
|
||||||
var authors = File.ReadAllLines(path)
|
|
||||||
|
|
||||||
.Skip(1) // Skip the first line which contains the column titles
|
|
||||||
|
|
||||||
.Select(line => // Using lambda expression, return the new Author objects
|
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
var columns = line.Split(',');
|
|
||||||
|
|
||||||
return new Author
|
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
Id = int.Parse(columns[0]),
|
|
||||||
|
|
||||||
Name = columns[1],
|
|
||||||
|
|
||||||
Description = columns[2]
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
.ToList(); // Turn the resulting IEnumerable into a list
|
|
||||||
|
|
||||||
authors is now an in-memory list containing all the authors from the original csv file. Note how methods can be chained!
|
|
||||||
|
|
||||||
# Query Syntax
|
|
||||||
|
|
||||||
An alternative way of using LINQ is with the __query syntax__
|
|
||||||
|
|
||||||
The following performs filtering to persons object:
|
|
||||||
|
|
||||||
var queryResult = from person in persons
|
|
||||||
|
|
||||||
where person.country == "Finland"
|
|
||||||
|
|
||||||
select person;
|
|
||||||
|
|
||||||
The __range variable __ person works like the range variable in a foreach loop in that it holds a copy of the current element in the persons variable
|
|
||||||
|
|
||||||
The where keyword specifies the condition for the filter
|
|
||||||
|
|
||||||
# LINQ Queries - Example
|
|
||||||
|
|
||||||
Suppose we have a class City that contains a property Description
|
|
||||||
|
|
||||||
The following would print the descriptions of all cities that have descriptions less than 100 characters long
|
|
||||||
|
|
||||||
IEnumerable Cities = new List<City> {/* Insert data here */};
|
More LINQ methods can be found [here](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable?view=net-7.0)
|
||||||
|
|
||||||
var filteredResult = from city in Cities
|
|
||||||
|
|
||||||
where city.Description.Length < 100
|
|
||||||
|
|
||||||
select city;
|
|
||||||
|
|
||||||
foreach (City result in filteredResult)
|
|
||||||
|
|
||||||
Console.WriteLine(result.Description);
|
|
||||||
|
|
||||||
# Extension Methods vs Query Syntax
|
### LINQ: A chained example
|
||||||
|
|
||||||
|
* Suppose we have a csv file `authors.csv` in our project directory.
|
||||||
|
```csharp
|
||||||
|
// using System.IO;
|
||||||
|
string path = @"C:\some\path\authors.csv";
|
||||||
|
var authors = File.ReadAllLines(path)
|
||||||
|
.Skip(1) // Skip the first line which contains the column titles
|
||||||
|
.Select(line => // Using lambda expression, return the new Author objects
|
||||||
|
{
|
||||||
|
var columns = line.Split(',');
|
||||||
|
return new Author
|
||||||
|
{
|
||||||
|
Id = int.Parse(columns[0]),
|
||||||
|
Name = columns[1],
|
||||||
|
Description = columns[2]
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.ToList(); // Turn the resulting IEnumerable into a list
|
||||||
|
```
|
||||||
|
* `authors` now contains all the authors from the original csv file!
|
||||||
|
|
||||||
|
## Query syntax
|
||||||
|
|
||||||
|
* An alternative way of using LINQ is with the *__query syntax__*
|
||||||
|
* The following performs filtering to a `persons` object:
|
||||||
|
```csharp
|
||||||
|
var queryResult = from person in persons
|
||||||
|
where person.country == "Finland"
|
||||||
|
select person;
|
||||||
|
```
|
||||||
|
* The *__range variable__* `person` works like the range variable in a `foreach` loop in that it holds a copy of the current element in the `persons` variable
|
||||||
|
* The `where` keyword specifies the condition for the filter
|
||||||
|
|
||||||
|
### LINQ queries: An example
|
||||||
|
|
||||||
|
* Suppose we have a class City that contains a property Description
|
||||||
|
* The following would print the descriptions of all cities that have descriptions less than 100 characters long
|
||||||
|
```csharp
|
||||||
|
IEnumerable Cities = new List<City> {/* Insert data here */};
|
||||||
|
|
||||||
|
var filteredResult = from city in Cities
|
||||||
|
where city.Description.Length < 100
|
||||||
|
select city;
|
||||||
|
|
||||||
|
foreach (City result in filteredResult)
|
||||||
|
Console.WriteLine(result.Description);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Extension methods vs. query syntax
|
||||||
|
|
||||||
* In the previous examples, we used the query syntax of LINQ
|
* In the previous examples, we used the query syntax of LINQ
|
||||||
* Both the methods and queries do pretty much the same thing
|
* Both the methods and queries do pretty much the same thing
|
||||||
* It is up to you which syntax you want to use
|
* It is up to you which syntax you want to use
|
||||||
* The method syntax works like any normal C# methods
|
* The method syntax works like any normal C## methods
|
||||||
* The query syntax might be more approachable to those who are familiar with SQL
|
* The query syntax might be more approachable to those who are familiar with SQL
|
||||||
|
|
||||||
|
<div class='columns' markdown='1'>
|
||||||
|
<div markdown='1'>
|
||||||
|
|
||||||
|
```csharp
|
||||||
var methodResult = persons
|
var methodResult = persons
|
||||||
|
.Where(person =>
|
||||||
|
person.Country == "Finland");
|
||||||
|
```
|
||||||
|
|
||||||
.Where(person => person.Country == "Finland");
|
</div>
|
||||||
|
<div markdown='1'>
|
||||||
|
|
||||||
|
```csharp
|
||||||
var queryResult = from person in persons
|
var queryResult = from person in persons
|
||||||
|
where person.Country == "Finland"
|
||||||
|
select person;
|
||||||
|
```
|
||||||
|
|
||||||
where person.Country == "Finland"
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
select person;
|
* All queries listed here: [https://www.tutorialsteacher.com/linq/linq-standard-query-operators](https://www.tutorialsteacher.com/linq/linq-standard-query-operators)
|
||||||
|
|
||||||
All queries listed here: [https://www.tutorialsteacher.com/linq/linq-standard-query-operators](https://www.tutorialsteacher.com/linq/linq-standard-query-operators)
|
## `ToArray()` and `ToList()` methods
|
||||||
|
|
||||||
# ToArray() and ToList() Methods
|
* Notice that the LINQ queries return an `IEnumerable`
|
||||||
|
* If you need to use arrays or lists, you need to call the `ToArray()` or `ToList()` methods
|
||||||
|
|
||||||
Notice that the LINQ queries return an IEnumerable
|
<div class='columns32' markdown='1'>
|
||||||
|
<div markdown='1'>
|
||||||
If you need to use arrays or lists, you need to call the ToArray() or ToList() methods:
|
|
||||||
|
|
||||||
string[] strings = new string[] { "Timo", "Pekka", "Taina", "Kalle" };
|
|
||||||
|
|
||||||
string[] queryResult = strings
|
|
||||||
|
|
||||||
.Where(s => s.StartsWith('T'));
|
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
string[] strings = new string[]
|
||||||
|
{ "Timo", "Pekka", "Taina", "Kalle" };
|
||||||
|
string[] queryResult = strings
|
||||||
|
.Where(s => s.StartsWith('T'));
|
||||||
|
```
|
||||||

|

|
||||||
|
|
||||||
string[] strings = new string[] { "Timo", "Pekka", "Taina", "Kalle" };
|
</div>
|
||||||
|
<div markdown='1'>
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
string[] strings = new string[]
|
||||||
|
{ "Timo", "Pekka", "Taina", "Kalle" };
|
||||||
string[] queryResult = strings
|
string[] queryResult = strings
|
||||||
|
.Where(s => s.StartsWith('T'))
|
||||||
|
.ToArray();
|
||||||
|
```
|
||||||
|
|
||||||
.Where(s => s.StartsWith('T'))
|
No error!
|
||||||
|
|
||||||
.ToArray(); // This works
|
|
||||||
|
|
||||||
# Exercise 2: Filtering Names
|
|
||||||
|
|
||||||
Download this file of names and add it to your project folder: [https://raw.githubusercontent.com/dominictarr/random-name/master/names.txt](https://raw.githubusercontent.com/dominictarr/random-name/master/names.txt)
|
|
||||||
|
|
||||||
Read all the contents into a string array with File.ReadAllLines()
|
|
||||||
|
|
||||||
Create a main loop where the user is asked for a string. Print the total number of names which contain that string.
|
|
||||||
|
|
||||||
If there are less than 10 resulting names, print the names as well
|
|
||||||
|
|
||||||
# Exercise 3: Queries on Object Lists
|
|
||||||
|
|
||||||
Expand on the exercise 2.
|
|
||||||
|
|
||||||
Create a new class User with two properties, int Id and string Name
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
If the number of filtered names is less than 10, create a list of Users with those names and a running Id
|
|
||||||
|
|
||||||
Sort the list of users by the length of the Name property
|
|
||||||
|
|
||||||
Print the names and id:s of the users in the sorted list
|
|
||||||
|
|
||||||
# Going Further: Extension Methods
|
## Exercise 2: Filtering Names
|
||||||
|
<!-- _backgroundColor: #29366f -->
|
||||||
|
|
||||||
Recap: IEnumerable itself only contains one method
|
* Download this file of names and add it to your project folder: [https://raw.githubusercontent.com/dominictarr/random-name/master/names.txt](https://raw.githubusercontent.com/dominictarr/random-name/master/names.txt)
|
||||||
|
* Read all the contents into a string array with `File.ReadAllLines()`
|
||||||
|
* Create a main loop where the user is asked for a string. Print the total number of names which contain that string.
|
||||||
|
* If there are less than 10 resulting names, print the names as well!
|
||||||
|
|
||||||
How does the LINQ library suddenly add all these methods to our Enumerables?
|
## Exercise 3: Queries on Object Lists
|
||||||
|
<!-- _backgroundColor: #29366f -->
|
||||||
|
|
||||||
This is possible with extension methods:
|
* Expand on the exercise 2.
|
||||||
|
* Create a new class User with two properties, int Id and string Name
|
||||||
|
* If the number of filtered names is less than 10, create a list of Users with those names and a running Id
|
||||||
|
* Sort the list of users by the length of the Name property
|
||||||
|
* Print the names and id:s of the users in the sorted list
|
||||||
|
|
||||||
[https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods)
|
## Going Further: Extension Methods
|
||||||
|
|
||||||
|
* Recap: `IEnumerable` itself only contains one method
|
||||||
|
* How does the LINQ library suddenly add all these methods to our Enumerables?
|
||||||
|
* This is possible with [extension methods](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods)!
|
Loading…
Reference in New Issue