Getting Started with LINQ (2/3)

mirzaleka

Mirza Leka

Posted on July 29, 2024

Getting Started with LINQ (2/3)

Language Integrated Query or LINQ is a C# feature that allows you to query different data sources using a unified language syntax.

In the first part we learned what is LINQ, when it is used and went through every-day operations. You can learn all about it in the article below:

In this blog we're moving forward with LINQ:

  • Set Operations
  • Quantifiers
  • Aggregation

Once again, we'll be working a List<Student> imported from JSON. Here, you can aquire the data as well as learn how to import it into C#.

SET OPERATIONS

LINQ Provides set operators to perform mathematical set operations on sequences or collections.

Distinct

The Distinct operator is used to filter out (remove) the duplicate elements from the collection:



IEnumerable<string> countries = students.Select(s => s.Country );
// ["Bosnia", "Bosnia", "UK", "Turkey", "India", "Turkey"...]

IEnumerable<string> distinctCountries = students.Select(s => s.Country).Distinct();
// ["Bosnia", "UK", "Turkey", "India", "Egypt"...]


Enter fullscreen mode Exit fullscreen mode

DistinctBy

The DistinctBy operator takes a predicate that specifies the condition for the distinction:



IEnumerable<Student> distinctStudents = students.DistinctBy(s => s.Country)
// collection of students from distinct countries


Enter fullscreen mode Exit fullscreen mode

distinct-students

This can be applied to a collection of countries as well:



var distinctCountries = students.DistinctBy(s => s.Country).Select(s => s.Country);
// unique countries


Enter fullscreen mode Exit fullscreen mode

Union

The Union operator joins multiple collections into one leaves out the duplicate values. To make things more interesting I created another collection of students that we'll use alongside the original:



var newStudents = new List<Student>
{
    new Student() { ID = 11, Name = "Anz", Age = 19, Country = "Australia" },
    new Student() { ID = 12, Name = "Mohamed", Age = 23, Country = "Egypt" },
    new Student() { ID = 13, Name = "Amar", Age = 24, Country = "Bosnia" }
};


Enter fullscreen mode Exit fullscreen mode

Let's say we want to take countries from both collections and create union of the two. Here's how that'd be done:



var originalStudentsCountries = students.Select(s => s.Country);
var newStudentsCountries = newStudents.Select(s => s.Country);

var countriesUnion = originalStudentsCountries.Union(newStudentsCountries);


Enter fullscreen mode Exit fullscreen mode

As we can, the new collection is combination of the two other collections without duplicate entries.
countries-union

UnionBy

The UnionBy speeds things up by reducing the number of steps needed to make the union.



var countriesUnion2 = students.UnionBy(newStudents, s => s.Country);
// the same result as above


Enter fullscreen mode Exit fullscreen mode

Intersect

The Intersect operator is used to compare two collections and find the elements that are present in both sets. We'll once again compare the countries in two collections and inspect the result:



var originalStudentsCountries = students.Select(s => s.Country);
var newStudentsCountries = newStudents.Select(s => s.Country);

var countriesIntersection = originalStudentsCountries.Intersect(newStudentsCountries);
// ["Bosnia", "Egypt"]


Enter fullscreen mode Exit fullscreen mode

The outcome is the distinct list of countries that are present in both collections.
countries-intersection

IntersectBy

The logic can once again be simplified by applying the IntersectBy operator:



var countriesIntersection2 = students
    .IntersectBy(newStudents.Select(ns => ns.Country), s => s.Country);
// the same result as above


Enter fullscreen mode Exit fullscreen mode

Except

The Except operator returns a combination of elements that are present in one collection, but not in the other. Basically the opposite of Intersect.



var originalStudentsCountries = students.Select(s => s.Country);
var newStudentsCountries = newStudents.Select(s => s.Country);

var countriesException = originalStudentsCountries.Except(newStudentsCountries);



Enter fullscreen mode Exit fullscreen mode

The output is the list of countries present in the first, but not present in the second collection.
countries-exception

ExceptBy

The ExceptBy achieves the same as Expect with less steps:



var countriesException2 = students
    .ExceptBy(newStudents.Select(ns => ns.Country), s => s.Country);
// the same result as above


Enter fullscreen mode Exit fullscreen mode

QUANTIFIERS

Contains

The Contains operator is used to validate if a specific item exists in the collection. For example, we can confirm that we have at least one student from Bosnia:



bool existingStudent = students.Select(s => s.Country).Contains("Bosnia"); 
// true


Enter fullscreen mode Exit fullscreen mode

But what we don't have is a student that is 100 years old.



bool nonExistingStudent = students.Select(s => s.Age).Contains(100); 
// false


Enter fullscreen mode Exit fullscreen mode

Any

Similarly to Contains, the Any operator is used to confirm if there is at least one student that meets specified criteria.



bool atLeastOneFromUK = students.Any(s => s.Country == "UK"); 
// true


Enter fullscreen mode Exit fullscreen mode

All

The All operator is used to verify if all elements in the collection meet specified criteria:



bool allEighteenOrAbove = students.All(s => s.Age >= 18); 
// true


Enter fullscreen mode Exit fullscreen mode

AGGREGATION

Count

The Count is used to to add together all the elements in a collection:



int totalStudentsCount = students.Count();

Console.WriteLine(totalStudentsCount); // 10

int studentsFromTurkeyCount = students.Where(s => s.Country == "Turkey").Count();

Console.WriteLine(studentsFromTurkeyCount); // 2


Enter fullscreen mode Exit fullscreen mode

Min, Max, Average

These operators are used to determine the minimum, maximum and average element in the collection.



var ages = students.Select(s => s.Age);

var youngestAge = ages.Min(); // 18
var oldestAge = ages.Max(); // 24
var averageAge = ages.Average(); // 19.89


Enter fullscreen mode Exit fullscreen mode

Sum

The Sum operator is used to aggregate all elements in the collections:



var ages = students.Select(s => s.Age);
int agesSum = ages.Sum();

Console.WriteLine(agesSum); // 199


Enter fullscreen mode Exit fullscreen mode

GroupBy

The GroupBy operator is used to group items in the collection by one or more properties. For example, earlier we learned that we can retrieve a list of unique countries:



var countries = students
    .Select(s => s.Country)
    .Distinct();


Enter fullscreen mode Exit fullscreen mode

unique-countries

The same can be achieved using the GroupBy operator.



var countries2 = students
    .GroupBy(s => s.Country)
    .Select(group => group.Key);


Enter fullscreen mode Exit fullscreen mode
  • GroupBy groups by specified properties (Country)
  • Select is used to read values out of groupped collection
  • group is a groupped collection (IEnumerable<IGroupping<T>>)
  • The Key is the groupping property (Country)

The end result is the same as above.

Here is another example of using Groupby operator to calculate the occurrences of each country in the collection:



var countryOccurrences = students
    .GroupBy(s => s.Country)
    .Select(g => new { Country = g.Key, Occurrences = g.Count() });


Enter fullscreen mode Exit fullscreen mode

Since the groupped collection (g) is an IEnumerable, we can call any of the LINQ operators (in this case Count).

country-occurrences-example

Lastly, we can group the students by their age in dictionary structure, like:



{
  18: [List of students that are 18 years old],
  19: [List of students that are 19 years old],
  ...
}


Enter fullscreen mode Exit fullscreen mode


var studentsGrouppedByAge = students
    .GroupBy(s => s.Age) // group by age
    .ToDictionary(g => g.Key, g => g.ToList() // form a dictionary
        .Select(s => new { Name = s.Name, Country = s.Country })); // select specific properties


Enter fullscreen mode Exit fullscreen mode

groupped-by-age

Furthermore, we can print each group and each student by using a combination of foreach loops:



foreach (var studentsGroup  in studentsGrouppedByAge)
{
    foreach (var student in studentsGroup.Value) // we use .Value because studentsGroup is a dictionary
    {
        Console.WriteLine($"Name: {student.Name}, Country: {student.Country}");
    }
}


Enter fullscreen mode Exit fullscreen mode

foreach-group

Other Chapters:

In the coming parts we'll dive deeper into LINQ.

Don't forget to hit the follow button. Also, follow me on Twitter to stay up to date with my upcoming content.

Bye for now 👋

💖 💪 🙅 🚩
mirzaleka
Mirza Leka

Posted on July 29, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Getting Started with LINQ (2/3)
webdev Getting Started with LINQ (2/3)

July 29, 2024