Speed ​​Dating with LINQ

A brief description of the main LINQ methods in simple language, without the terms “projection”, “predicate”, “delegate”, etc. We couldn't get rid of the term “lambda expression” 🙂

What is LINQ

LINQ (Language Integrated Query) is a set of methods for working with collections.

For simplicity, we will call a collection any object that stores several other objects. This could be an array of numbers, or the result of a database query, or a set of elements read from an XML document, etc.

LINQ methods are called in the source code like other methods, an example in C#:

var result = nums.Select( n => n*2 );

What is a lambda expression

In the previous example, the Select method is called, followed by the text in brackets – this is a lambda expression, a short form for describing a function. Before =>, a list of input parameters is specified, in this example there is one input parameter, it is designated n. After =>, the program code is specified that uses the input parameters and returns the calculated value. In this example, the function returns the value of the input parameter multiplied by 2.

LINQ Method Call Chains

For most LINQ methods, the result is also a collection, to which you can apply another LINQ method, for example:

var result = nums.Select( n => n*2 ).Where( n => n > 4 ).OrderBy( n => n );

This way you can “chain” LINQ methods one after another many times.

Method Syntax and Query Syntax

In the previous example, LINQ methods are called like any other method in the C# language. This syntax is called Method Syntax.

LINQ method calls can be described in a simpler form using Query Syntax, for example:

var result = from n in nums
                   where n*2 > 4
                   orderby n
                   select n*2;

The LINQ query syntax is similar to SQL, but it is not SQL, it is a different syntax, and you don't need to look for a match.

Below is a brief description of the main LINQ methods.

Select – Cycle through a collection

Description of the example collection:

// создать коллекцию из 4-х чисел
int[] nums = { 1, 2, 3, 4 }; 

Query Syntax

Example in query syntax:

var result1 = from n in nums select n+1;

Analysis of the example in parts:

from n in nums
corresponds
For each element n in the original collection nums

select n+1
corresponds
add the value obtained from the expression n+1 to the resulting collection

Final analysis of the example:

var result1 = from n in nums select n+1

corresponds
For each element n in the original collection nums
add the value obtained from the expression n+1 to the resulting collection.
Place the resulting collection into the result1 variable.

Method Syntax

The previous example in method syntax:

var result2 = nums.Select( n => n+1 );

The Select method defines a function in parentheses that returns an element of the resulting collection.

The input parameter of the function is after the opening bracket and before =>. In this example, the input parameter of the function is the variable n.

The input parameter of the function is an element of the source collection.

Analysis of the example in parts:

nums.Select
corresponds
For each element in the original collection nums

( n
corresponds
using the variable n to denote an element of the collection nums,

=> n+1
corresponds
add the value obtained from the expression n+1 to the resulting collection

Final analysis of the example:

var result2 = nums.Select( n => n+1 );

corresponds
For each element in the original collection nums
using the variable n to denote an element of the collection nums,
add the value obtained from the expression n+1 to the resulting collection.
Place the resulting collection into the result2 variable.

In both variants (query syntax and method syntax) the resulting collection contains numbers: 2, 3, 4, 5

SelectMany – Nested Loop (Cartesian Product)

Description of collections for example:

int[] nums = { 1, 2, 3, 4 }; // создать коллекцию из 4-х чисел
string[] chars = { "a", "b" }; // создать коллекцию из 2-х строк

Query Syntax

Example in query syntax:

var result3 = from n in nums
              from c in chars
              select c + n.ToString();

Analysis of the example in parts:

from n in nums
from c to chars
corresponds
For each element n in the original collection nums
and for each element c in the original collection chars

select c + n.ToString()
corresponds
add the value obtained from the expression c + n.ToString() to the resulting collection

Final analysis of the example:

var result3 = from n in nums
              from c in chars
              select c + n.ToString();

corresponds
For each element n in the original collection nums
and for each element c in the original collection chars
add the value obtained from the expression c + n.ToString() to the resulting collection.
Place the resulting collection into the result3 variable.

Method Syntax

The previous example in method syntax:

var result4 = nums.SelectMany( n => chars.Select( c => c + n.ToString() ));

In the SelectMany method, the parameter (i.e. in the brackets) describes a function that returns a collection.
The resulting collection is formed by concatenating all collections obtained by calling the function described in the parentheses.
The input parameter of the function is after the opening bracket and before =>. In this example, the input parameter of the function is the variable n.
The input parameter of the function is an element of the source collection. In this example, the source collection is nums, and the variable n is an element of the nums collection.

Analysis of the example in parts:

nums.SelectMany
corresponds
For each element in the original collection nums

( n
corresponds
using the variable n to denote an element of the collection nums,

=>
corresponds
add to the resulting collection the objects of the collection obtained by the following algorithm:

chars.Select
corresponds
For each element in the original collection chars

( c
corresponds
using the variable c to denote an element of the chars collection,

=> c + n.ToString()
corresponds
add the value obtained from the expression c + n.ToString() to the resulting collection

Final analysis of the example:

var result4 = nums.SelectMany( n => chars.Select( c => c + n.ToString() ));

corresponds
For each element in the original collection nums
using the variable n to denote an element of the collection nums,
add to the resulting collection the objects of the collection obtained by the following algorithm:
For each element in the original collection chars
using the variable c to denote an element of the chars collection,
add the value obtained from the expression c + n.ToString() to the resulting collection.
Place the resulting collection into the result4 variable.

In both variants (query syntax and method syntax) the resulting collection contains the rows: a1, b1, a2, b2, a3, b3, a4, b4

Another example of the SelectMany method

Description of the example collection:

/* создать коллекцию из 2 объектов, 
   где каждый объект описывает категорию товаров 
   и содержит поля Name (название категории) и Goods (список названий товаров) */

var categories = new[] { new { Name = "Flowers", 
                               Goods = new string[] { "Rose", "Astra", "Tulip" } },
                         new { Name = "Candies", 
                               Goods = new string[] { "Bonbon", "Chocolate" } } };

Example in query syntax:

var goods1 = from c in categories
             from g in c.Goods
             select g;

The previous example in method syntax:

var goods2 = categories.SelectMany(c => c.Goods );

In both variants (query syntax and method syntax), the resulting collection contains a common list of product names: Rose, Astra, Tulip, Bonbon, Chocolate

Where – Search for items in a collection based on search conditions

Description of the example collection:

// создать коллекцию из 3-х строк
string[] names = { "Tom", "Bob", "Nick" }; 

Query Syntax

Example in query syntax:

var foundNames1 = from n in names
                 where n.Contains("o")
                 select n;

Analysis of the example in parts:

from n in names
corresponds
For each element n in the original collection names

where n.Contains(“o”)
corresponds
check the condition n.Contains(“o”) (the value of n contains the letter o).

select n;
corresponds
If the condition is met, add the value n to the resulting collection.

Final analysis of the example:

var foundNames1 = from n in names
                 where n.Contains("o")
                 select n;

corresponds
For each element n in the original collection names
check the condition n.Contains(“o”) (the value of n contains the letter o).
If the condition is met, add the value n to the resulting collection.
Place the resulting collection into the foundNames1 variable.

Method Syntax

The previous example in method syntax:

var foundNames2 = names.Where( n => n.Contains("o") );

The Where method contains parentheses that describe a function that returns true or false.
The input parameter of the function is after the opening bracket and before =>. In this example, the input parameter of the function is the variable n.
The input parameter of the function is an element of the source collection.
The Where method returns a collection of elements for which the function in parentheses returned true.

Analysis of the example in parts:

names.where
corresponds
For each element in the original collection names

( n
corresponds
using the variable n to denote an element of the names collection,

=> n.Contains(“o”)
corresponds
check the condition n.Contains(“o”) (the value of n contains the letter o).
If the condition is met, add the value n to the resulting collection.

Final analysis of the example:

var foundNames2 = names.Where( n => n.Contains("o") );

corresponds
For each element in the original collection names
using the variable n to denote an element of the names collection,
check the condition n.Contains(“o”) (the value of n contains the letter o).
If the condition is met, add the value n to the resulting collection.
Place the resulting collection into the foundNames2 variable.

In both variants (query syntax and method syntax) the resulting collection contains the rows: Tom, Bob

Single – Find a single item in a collection

Description of the example collection:

int[] nums = { 1, 2, 3, 4 }; // создать коллекцию из 4-х чисел

Query Syntax

The query syntax does not include operators for retrieving a single element from a collection.

Method Syntax

Example in method syntax:

var result5 = nums.Single( n => n == 3 );

The Single method contains a function in parentheses that returns true or false.
The input parameter of the function is after the opening bracket and before =>. In this example, the input parameter of the function is the variable n.
The input parameter of the function is an element of the source collection.
The Single method returns the element of the collection for which the function in parentheses returned true. If there are 0 or more than 1 such elements, an exception will be thrown.

If the Single method has empty parentheses, the method simply checks that there is one element in the collection and returns it.

Analysis of the example in parts:

nums.single
corresponds
For each element in the original collection nums

( n
corresponds
using the variable n to denote an element of the collection nums,

=> n == 3
corresponds
check the condition n == 3 (the value of n is 3)

Final analysis of the example:

var result5 = nums.Single( n => n == 3 );

corresponds
For each element in the original collection nums
using the variable n to denote an element of the collection nums,
check the condition n == 3 (the value of n is 3).
If the condition is met for a single element, place the value of that element into the result5 variable.

In this example, the variable result5 contains the number: 3

Join – Combine two source collections into one collection by finding matches between the elements of the source collections

Description of collections for example:

/* создать коллекцию из 3-х объектов, 
   где каждый объект описывает студента 
   и содержит поля Name (имя студента) и FacultyId (код факультета) */

var students = new[] { new { Name = "Tom", FacultyId = 1 }, 
                     new { Name = "Bob", FacultyId = 2 }, 
                     new { Name = "John", FacultyId = 2 } };  

/* создать коллекцию из 2-х объектов, 
   где каждый объект описывает факультет 
   и содержит поля Name (название факультета) и Id (код факультета) */

var faculties = new[] { new { Name = "Faculty of Mathematics", Id = 1 }, 
                       new { Name = "Faculty of Physics",     Id = 2 } };  

Query Syntax

Example in query syntax:

var result10 = from s in students
               join f in faculties
               on s.FacultyId equals f.Id
               select new { Name = s.Name, Faculty = f.Name };

Analysis of the example in parts:

from s in students
join f in faculties
corresponds
For each object s in the original collection students
and for each object f in the original collection faculties

on s.FacultyId equals f.Id
corresponds
check the condition – the value of the FacultyId field (faculty code) in the students collection object is equal to the value of the Id field (faculty code) in the faculties collection object.

select new { Name = s.Name, Faculty = f.Name };
corresponds
If the comparison condition is met, add an object to the resulting collection where the Name field contains the student's name from the Name field in the students collection object, and the Faculty field contains the faculty name from the Name field in the faculties collection object.

Final analysis of the example:

var result10 = from s in students
               join f in faculties
               on s.FacultyId equals f.Id
               select new { Name = s.Name, Faculty = f.Name };

corresponds
For each object s in the original collection students
and for each object f in the original collection faculties
check condition – value of the FacultyId field (faculty code) in the students collection object
equal to the value of the Id field (faculty code) in the faculties collection object.
If the comparison condition is met, add an object to the resulting collection where the Name field contains the student's name from the Name field in the students collection object, and the Faculty field contains the faculty name from the Name field in the faculties collection object.
Place the resulting collection into the result10 variable.

Method Syntax

The previous example in method syntax:

var result11 = students.Join(faculties, 
                             s => s.FacultyId, 
                             f => f.Id, 
                             (s, f) => new 
                             { 
                               Name = s.Name, 
                               Faculty = f.Name 
                             });

In the Join method, the first parameter is a collection where matching elements will be searched for for each element of the original collection.
In this example, the faculties collection is passed as the first parameter.
Thus, for each object in the students collection, the corresponding objects in the faculties collection will be searched for.

The second parameter of the Join method describes a function that returns a value obtained from an object in the first collection, used to compare with an object in the second collection.
In this example, the second parameter of the Join method describes the function:
s => s.FacultyId
This function returns the value of the FacultyId field from the students collection object.

The third parameter of the Join method describes a function that returns a value obtained from the object of the second collection, used to compare with the object from the first collection.
In this example, the third parameter of the Join method describes the function:
f => f.Id
This function returns the value of the Id field (faculty code) from the faculties collection object.
Thus, when comparing a student object and a faculty object, the faculty code will be compared.

The fourth parameter of the Join method describes a function that receives as input an object of the first collection and an object of the second collection for which the comparison condition is met.
The function returns an object that is created for the given two objects. This object will be added to the resulting collection.
In this example, the fourth parameter of the Join method describes the function:
(s, f) => new { Name = s.Name, Faculty = f.Name }
Thus, the resulting collection will contain objects in which the Name field contains the student's name and the Faculty field contains the name of the faculty.

Analysis of the example in parts:

students.join(faculties
corresponds
For each object in the students collection
and for each object in the faculties collection

s => s.FacultyId,
f => f.Id,
corresponds
compare the value of the FacultyId field in the object of the first collection and the value of the Id field in the object of the second collection.

(s, f) => new { Name = s.Name, Faculty = f.Name }
corresponds
If the comparison condition is met, add an object to the resulting collection where the Name field contains the value of the Name field in the object of the first collection, and the Faculty field contains the value of the Name field in the object of the second collection.

Final analysis of the example:

var result11 = students.Join(faculties, 
                             s => s.FacultyId, 
                             f => f.Id, 
                             (s, f) => new { Name = s.Name, Faculty = f.Name });

corresponds
For each object in the students collection
and for each object in the faculties collection
compare the value of the FacultyId field in the object of the first collection and the value of the Id field in the object of the second collection.
If the comparison condition is met, add an object to the resulting collection where the Name field contains the value of the Name field in the object of the first collection, and the Faculty field contains the value of the Name field in the object of the second collection.
Place the resulting collection into the result11 variable.

In both variants (query syntax and method syntax), the resulting collection contains 3 objects, where each object describes a student and contains the Name (student name) and Faculty (faculty name) fields:

Name

Faculty

Tom

Faculty of Mathematics

Bob

Faculty of Physics

John

Faculty of Physics

GroupJoin – Combine the master table collection and the child table collection into one hierarchical collection

Description of collections for example:

/* создать коллекцию из 3-х объектов, 
   где каждый объект описывает отдел 
   и содержит поля Name (название отдела) и Id (код отдела) */

var departments = new[] { new { Name = "Purchasing Department", Id = 1 },
                     new { Name = "Sales Department", Id = 2 },
                     new { Name = "Analytics Department", Id = 3 } };

/* создать коллекцию из 4-х объектов, 
   где каждый объект описывает сотрудника 
   и содержит поля Name (имя сотрудника) и DepId (код отдела) */

var workers = new[] { new { Name = "Harry",     DepId = 1 },
                       new { Name = "George",   DepId = 1 },
                       new { Name = "Jessica",  DepId = 1 },
                       new { Name = "Emma",     DepId = 2 } };

Query Syntax

Example in query syntax:

var result12 = from d in departments
               join w in workers
               on d.Id equals w.DepId
               into foundWorkers
               select new 
               { 
                   DepName    = d.Name, 
                   DepWorkers = foundWorkers 
               };

Analysis of the example in parts:

from d in departments
corresponds
For each object d from the departments collection

join w in workers
on d.Id equals w.DepId
corresponds
find objects in the workers collection by condition:
the value of the Id field (department code) in the object from the departments collection is equal to the value of the DepId field (department code) in the object from the workers collection.

into foundWorkers
corresponds
For each object in the departments collection, save the search results in the foundWorkers variable.

select new
{
DepName = d.Name,
DepWorkers = foundWorkers
};
corresponds
For each object from the departments collection, create an object in which the DepName field contains the name of the department, and the DepWorkers field contains a collection of objects from the workers collection found for the given department.

Final analysis of the example:

var result12 = from d in departments
               join w in workers
               on d.Id equals w.DepId
               into foundWorkers
               select new 
               { 
                   DepName    = d.Name, 
                   DepWorkers = foundWorkers 
               };

corresponds
For each object d from the departments collection
find objects in the workers collection by condition:
the value of the Id field (department code) in the object from the departments collection is equal to the value of the Id field (department code) in the object from the workers collection.
For each object in the departments collection, save the search results in the foundWorkers variable.
For each object from the departments collection, create an object in which the DepName field contains the name of the department, and the DepWorkers field contains a collection of objects from the workers collection found for the given department.
Place the resulting collection into the result12 variable.

The resulting collection will also include those objects from the departments collection for which objects from the workers collection were not found.

Method Syntax

The previous example in method syntax:

var result13 = departments.GroupJoin(workers,
                                      d => d.Id,
                                      w => w.DepId,
                                      (d, foundWorkers) => new 
                                      { 
                                          DepName    = d.Name, 
                                          DepWorkers = foundWorkers 
                                      });

In the GroupJoin method, the first parameter is a collection where matching elements will be searched for for each element of the original collection.

The second parameter of the GroupJoin method describes a function that returns a value obtained from an object in the first collection, used to compare with an object in the second collection.
In this example, the second parameter of the GroupJoin method describes the function:
d => d.Id
This function returns the value of the Id field (department code) from the departments collection object.

The third parameter of the GroupJoin method describes a function that returns a value obtained from the object of the second collection, used to compare with the object from the first collection.
In this example, the third parameter of the GroupJoin method describes the function:
w => w.DepId
This function returns the value of the DepId field (department code) from the second workers collection object.
Thus, when comparing a department object and an employee object, the department code will be compared.

The fourth parameter of the GroupJoin method describes a function that receives as input an object of the first collection and a collection of objects of the second collection found for a given object of the first collection.
The function returns an object that will be added to the resulting collection.

In this example, the fourth parameter of the GroupJoin method describes the function:
(d, foundWorkers) => new
{
DepName = d.Name,
DepWorkers = foundWorkers
});

Thus, the resulting collection will contain objects in which the DepName field contains the name of the department, and the DepWorkers field contains a collection of objects from the workers collection found for the given department.

Analysis of the example in parts:

departments.GroupJoin(workers
corresponds
For each object in the original departments collection
find objects in the workers collection

d => d.Id,
w => w.DepId,
corresponds
by condition:
the value of the Id field (department code) in the object from the source departments collection is equal to the value of the DepId field (department code) in the object from the workers collection.

(d, foundWorkers) => new
{
DepName = d.Name,
DepWorkers = foundWorkers
});
corresponds
For each object from the source departments collection, create an object in which the DepName field contains the name of the department, and the DepWorkers field contains a collection of objects from the workers collection found for the given department.

Final analysis of the example:

var result13 = departments.GroupJoin(workers,
                                      d => d.Id,
                                      w => w.DepId,
                                      (d, foundWorkers) => new 
                                      { 
                                          DepName    = d.Name, 
                                          DepWorkers = foundWorkers 
                                      });

corresponds
For each object in the original departments collection
find objects in the workers collection
by condition:
the value of the Id field (department code) in the object from the source departments collection is equal to the value of the DepId field (department code) in the object from the workers collection.
For each object from the source departments collection, create an object in which the DepName field contains the name of the department, and the DepWorkers field contains a collection of objects from the workers collection found for the given department.
Place the resulting collection into the result13 variable.

In both variants (query syntax and method syntax), the resulting collection contains 3 objects (corresponding to 3 objects of the original departments collection), where each object describes a department and contains a DepName field (the name of the department) and a DepWorkers field (a collection of objects of the department's employees):

DepName

DepWorkers

Name

DepId

Purchasing Department

Harry

1

George

1

Jessica

1

Sales Department

Emma

2

Analytics Department

GroupBy – Split a collection into multiple collections, each containing elements with the same key field values

Description of the example collection:

/* создать коллекцию из 3-х объектов, 
   где каждый объект описывает студента и содержит поля Name и Age */
var people = new[] { new { Name = "Tom", Age = 24 }, 
                     new { Name = "Bob", Age = 25 }, 
                     new { Name = "John", Age = 25 } };  

Query Syntax

Example in query syntax:

var ageGroups1 = from p in people
                 group p.Name by p.Age;

Analysis of the example in parts:

from p in people
corresponds
From the original collection people
using the variable p to denote the people collection object,

group p.Name
corresponds
select groups of objects where each object is a value of p.Name

by p.Age
corresponds
while one group of objects includes values ​​obtained from objects of the original collection with the same value of the Age field

Final analysis of the example:

var ageGroups1 = from p in people
                 group p.Name by p.Age;

corresponds
From the original people collection,
using the variable p to denote the people collection object,
select groups of objects where each object is a value of p.Name
while one group of objects includes values ​​obtained from objects of the original collection with the same value of the Age field.
Place the resulting collection of object groups into the ageGroups1 variable.

Method Syntax

The previous example in method syntax:

var ageGroups2 = people.GroupBy( p => p.Age, p => p.Name );

The GroupBy method can have from one to three parameters.

The first parameter of the GroupBy method describes a function that defines the key for grouping.
In this example, the first parameter of the GroupBy method describes the function:
p => p.Age
This function returns the value of the Age field from the people collection object.
Thus, in this example, groups of objects will be created that correspond to objects in the people collection with the same values ​​for the Age field.

The second parameter of the GroupBy method describes a function that returns an object that will be added to the resulting group of elements.
This parameter is optional.
In this example, the second parameter of the GroupBy method describes the function:
p => p.Name
Thus, in this example, groups of objects will be created, where each object contains the value of the Name field from the people collection object.

If only the first parameter is specified in the GroupBy method (i.e. the second parameter of the method is missing), then the resulting groups of objects will be created from the objects in the source collection.

The third parameter of the GroupBy method describes a function that compares the key values ​​obtained from the function described in the first parameter of the GroupBy method. This parameter is optional.
If the GroupBy method does not have a third parameter, then the key values ​​are compared using a built-in mechanism.

Analysis of the example in parts:

people.groupby
corresponds
From the original collection people select groups of objects

( p => p.Age
corresponds
corresponding to objects of the people collection with the same value of the Age field.

p => p.Name
corresponds
In the resulting object groups, each object contains the value of the Name field from the people collection object.

Final analysis of the example:

var ageGroups2 = people.GroupBy( p => p.Age, p => p.Name );
corresponds
From the original collection people select groups of objects
corresponding to objects of the people collection with the same value of the Age field.
In the resulting object groups, each object contains the value of the Name field from the people collection object.
Place the resulting collection of object groups into the ageGroups2 variable.

In both variants (query syntax and method syntax), the resulting collection of object groups contains 2 object groups:
Key field: 24, objects in group: Tom
Key field: 25, objects in group: Bob, John

Brief description of other LINQ methods

Sorting methods

OrderBy – sort in ascending order
OrderByDescending – sort in descending order
ThenBy – sort in ascending order, taking into account the previous sort
ThenByDescending – sort in descending order, taking into account the previous sorting

Methods for calculating an aggregate (for example, the sum of the values ​​of all elements in a collection).

Sum – the sum of the values ​​of all elements of the collection
Average – the average value of all elements of the collection
Min – the minimum value of the collection element
Max – the maximum value of a collection element
Count – the number of elements in the collection
Aggregate – the result of an operation performed sequentially on all elements of a collection (for example, multiplying all elements by each other)

Methods for checking conditions on collection elements

All – return true if the condition is met for all elements of the collection
Any – return true if the condition is met for at least one element of the collection
Contains – return true if the collection contains the specified element

Collection Union and Intersection Methods

Concat – combine two collections, all elements of the two collections will be included in the resulting collection
Except – return a collection containing elements that are present in the first collection and not present in the second collection
Intersect – return a collection containing elements that are present in both the first and second collections
Union – combine two collections, eliminating duplication

Methods for skipping elements (analogs of pagination)

Skip – return a collection where the specified number of first elements are missing
SkipLast – return a collection where the specified number of last elements are missing
SkipWhile – exclude elements from the source collection, starting with the first one, while the specified condition is met. Return a collection containing all remaining elements, starting with the first one for which the condition is not met.
Take – return a collection containing a given number of first elements
TakeLast – return a collection containing a given number of last elements
TakeWhile – return a collection that includes elements from the source collection, starting with the first one, as long as the specified condition is met

Other methods

Cast – cast all elements of a collection to a specified type
Distinct – remove duplicate values ​​from a collection
ToArray, ToDictionary, ToList – convert a collection to a collection of another type
ToLookup – return a collection containing key-value pairs, representing a one-to-many dictionary, similar to an index in a database

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *