C# .NET

Fundamentals of LINQ in C#

Bhushan Kadam
Cover Image for Fundamentals of LINQ in C#

Introduction to LINQ

LINQ, or Language Integrated Query, is a set of features in C# that allows developers to perform querying and manipulating data in a more convenient and expressive way. LINQ provides a consistent and unified syntax for querying and manipulating various types of data, such as arrays, lists, XML documents, and databases.

In this blog post, we will take a look at some of the basics of working with LINQ in C#. We will also explore the different LINQ Operations that we can perform. In this part 1, we will look at the Filtering, Projection, Sorting, and Grouping operations.

Don't forget to visit Part 2 of this blog. In that blog, we will look at the remaining LINQ Operations like Grouping, Set Operations, Aggregations, etc.

A key feature of LINQ is to perform query operations on data collections using SQL-like syntax. This allows developers to write queries in C# code similar to SQL queries. It also makes it easier to understand and work with the data.

LINQ Operations

We can perform a wide range of operations on the collections in C# using LINQ. Some of the common operations include -

  1. Filtering: The Where method can be used to filter a collection based on a given predicate.

  2. Projection: The Select method can be used to transform the elements of a collection by applying a transformation function. The SelectMany method can be used to flatten a collection of collections.

  3. Sorting: The OrderBy, OrderByDescending, ThenBy, and ThenByDescending methods can be used to sort a collection based on one or more key functions.

  4. Grouping: The GroupBy method can be used to group the elements of a collection based on a specified key.

  5. Set operations: The Union, Intersect, Except, and Distinct methods can be used to perform set operations, such as union, intersection, and difference, between two collections.

  6. Aggregation: The Sum, Min, Max, Average, Count, and Aggregate methods can be used to perform various types of aggregation on a collection.

  7. Joining: The Join and GroupJoin methods can be used to join two collections based on a specified key.

  8. Partitioning: The Take and Skip methods can be used to partition a collection into smaller collections.

  9. Element operations: The First, Last, Single, ElementAt, and DefaultIfEmpty methods can be used to select specific elements from a collection.

  10. Quantifiers: The Any, All, Contains methods can be used to determine if a certain condition is true for any, all, or specific elements of the collection.

  11. Generation: The Range, Repeat, and Empty methods can be used to create a new collection based on given criteria.

These are just some of the many operations that can be performed with LINQ in C#, and the possibilities are almost endless as you can chain multiple operations together to achieve the desired outcome.

We will try to explore a few of the above-mentioned important LINQ Operations in detail.

Filtering

Filtering is one of the main features of LINQ which provides the ability to filter data based on certain criteria. This is done using the "Where" operator.

Here is an example of using the "Where" operator to filter a list of integers.

List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

//1. LINQ Where Operation Demo
var evenNumbers = numbers.Where(n => n % 2 == 0);

foreach (int num in evenNumbers)
{
    Console.WriteLine(num);
}

In this example, the "Where" operator is used to filter the list of numbers, resulting in a new list that contains only the even numbers. The lambda expression passed to the "Where" operator (n => n % 2 == 0) is used to define the filter criteria. The lambda expression takes a single parameter (n) and returns a Boolean value indicating whether the current number should be included in the filtered list (true) or not (false).

You can also chain multiple where conditions to filter the data even more. For example, we can extend the above example to return even numbers greater than 5.

List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

//2. LINQ - Chaining Where clause.
var evenNumbersGreaterThan5 = numbers.Where(n => n % 2 == 0).Where(n=>n>5);

foreach (int num in evenNumbersGreaterThan5)
{
    Console.WriteLine(num);
}

Projection

Projection is one of the main features of LINQ which provides the ability to perform projections. Projection is the process of selecting specific properties from an object or collection of objects. This is done using the "Select" operator.

Here is an example of using the "Select" operator to perform a projection on a list of strings -

List<string> names = new List<string> { "Steve", "James", "Ross", "Mike" };

// Use the Select operator to perform a projection on the list of names
var firstLetters = names.Select(n => n[0]);

foreach (char letter in firstLetters)
{
    Console.WriteLine(letter);
}

In this example, the "Select" operator is used to perform a projection on the list of names. This results in a new list that contains only the first letter of each name. The lambda expression passed to the "Select" operator (n => n[0]) is used to define the projection. The lambda expression takes a single parameter (n) and returns the first letter of the current name (n[0]).

You can also use the select operator to select multiple fields from the object. For example, let's say have a class Person with properties Name, Age, Country and you want to select only Name and Age from the collection of Person objects.

List<Person> people = new List<Person> { 
    new Person{Name = "James", Age = 25, Country = "United States"},
    new Person{Name = "Steve", Age = 35, Country = "Canada"},
    new Person{Name = "Ross", Age = 30, Country = "United Kingdom"}
};

//3. LINQ - Select Clause
var selectedFields = people.Select(p => new { p.Name, p.Age });

foreach (var person in selectedFields)
{
    Console.WriteLine($"{person.Name} is {person.Age} years old.");
}

In this example, the "Select" operator is used to perform a projection on the list of people, resulting in a new list that contains only the Name and Age of each person in the form of an anonymous type.

It's worth noting that the Select operator returns a new IEnumerable which doesn't affect the original collection and the original collection remains unchanged.

Sorting

Sorting is another important feature of LINQ. It provides the ability to sort data, which is the process of arranging data in a specific order based on one or more sorting criteria. This is done using the "OrderBy" and "OrderByDescending" operators.

Here is an example of using the "OrderBy" operator to sort a list of integers in ascending order -

List<int> numbers = new List<int> { 7, 4, 8, 2, 9, 1, 6, 3, 5, 10 };

//4. LINQ - Use the OrderBy operator to sort the list of numbers in ascending order
var sortedNumbers = numbers.OrderBy(n => n);

foreach (int num in sortedNumbers)
{
    Console.WriteLine(num);
}

In this example, the "OrderBy" operator is used to sort the list of numbers in ascending order, resulting in a new list that contains the numbers in sorted order. The lambda expression passed to the "OrderBy" operator (n => n) is used to define the sorting criteria. The lambda expression takes a single parameter (n) and returns the current number (n).

If you want to sort the list of numbers in descending order you can use the "OrderByDescending" operator.

List<int> numbers = new List<int> { 7, 4, 8, 2, 9, 1, 6, 3, 5, 10 };

// Use the OrderByDescending operator to sort the list of numbers in descending order
var sortedNumbers = numbers.OrderByDescending(n => n);

foreach (int num in sortedNumbers)
{
    Console.WriteLine(num);
}

You can also use multiple sorting criteria. For example, if you want to sort the list of people based on multiple properties like Age, Name, then we can do that using LINQ.

List<Person> people = new List<Person> { 
    new Person{Name = "John", Age = 25},
    new Person{Name = "James", Age = 35},
    new Person{Name = "Rob", Age = 30},
    new Person{Name = "Steve", Age = 25}
};

// Use the OrderBy and ThenBy operator to sort the list of people
var sortedPeople = people.OrderBy(p => p.Age).ThenBy(p => p.Name);

foreach (var person in sortedPeople)
{
    Console.WriteLine($"{person.Name} is {person.Age} years old.");
}

In this example, the "OrderBy" operator is used to sort the list of people based on their age in ascending order, and the "ThenBy" operator is used to sort the list of people based on their names in ascending order.

It's worth noting that like the Select operator, the OrderBy and OrderByDescending return a new IEnumerable which doesn't affect the original collection and the original collection remains unchanged.

Grouping

One of the main features of LINQ is the ability to group data based on certain criteria. This is done using the "GroupBy" operator.

Here is an example of using the "GroupBy" operator to group a list of integers by their remainder when divided by 3 -

List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// Use the GroupBy operator to group the list of numbers by remainder when divided by 3
var groupedNumbers = numbers.GroupBy(n => n % 3);

foreach (var group in groupedNumbers)
{
    Console.WriteLine("Remainder " + group.Key + ": ");
    foreach (int num in group)
    {
        Console.Write(num + " ");
    }
    Console.WriteLine();
}

In this example, the "GroupBy" operator is used to group the list of numbers based on the remainder when divided by 3, resulting in three groups: one group for numbers with a remainder of 0, one group for numbers with a remainder of 1, and one group for numbers with a remainder of 2. The lambda expression passed to the "GroupBy" operator (n => n % 3) is used to define the grouping criteria. The lambda expression takes a single parameter (n) and returns the remainder of n when divided by 3.

You can also use an object to group the data. For example, you have a list of people and you want to group them by their country.

List<Person> people = new List<Person> { 
    new Person{Name = "John", Age = 25, Country = "United States"},
    new Person{Name = "James", Age = 35, Country = "Canada"},
    new Person{Name = "Rob", Age = 30, Country = "United Kingdom"},
    new Person{Name = "Steve", Age = 25, Country = "United States"}
};

// Use the GroupBy operator to group the list of people by country
var groupedPeople = people.GroupBy(p => p.Country);

foreach (var group in groupedPeople)
{
    Console.WriteLine("Country: " + group.Key);
    foreach (var person in group)
    {
        Console.WriteLine("\t" + person.Name);
    }
}

In this example, the "GroupBy" operator is used to group the list of people based on their country, resulting in three groups: one group for people from the United States, one group for people from Canada, and one group for people from the United Kingdom. The lambda expression passed to the "GroupBy" operator (p => p.Country) is used to define the grouping criteria. The lambda expression takes a single parameter (p) and returns the country of the person (p.Country).

It's worth noting that like the Select and OrderBy, the GroupBy operator return a new IEnumerable which doesn't affect the original collection and the original collection remains unchanged.

Conclusion

In this blog, we learned about the LINQ and how flexible and easy it is to use in C# to perform various operations on the collections. We also studied some of the important LINQ operations. Don't forget to check out Part 2 of this blog where we will look into the remaining LINQ Operations.

I have tried to cover most of the important LINQ operations, in this blog, but you can always check more on Microsoft Official Documentation.

Thank you for taking the time to read this blog post. We hope that you found the information provided in the blog to be helpful and informative. For similar content, please check out our other blogs.

We appreciate your support and feedback, and we would love to hear from you if you have any questions or comments about the blog. If you have any specific topic you want us to cover in the future, please feel free to let us know.

Once again, thank you for reading our blog, and we look forward to your continued support.