C# -

LINQ

LINQ (Language Integrated Query) is a powerful feature in C# that provides a consistent way to query data from various sources such as collections, databases, XML documents, and more. LINQ allows you to write queries directly in C#, utilizing the language's syntax and type checking, resulting in more readable and maintainable code.


1. Understanding LINQ

LINQ provides a uniform query syntax to retrieve and manipulate data from different sources. It integrates querying capabilities into the C# language, allowing you to work with data in a more intuitive and type-safe manner.

Basic LINQ Query:
        
            namespace LinqExamples;

class Program
{
    static void Main(string[] args)
    {
        var numbers = new List<int> { 1, 2, 3, 4, 5 };

        var evenNumbers = from num in numbers
                          where num % 2 == 0
                          select num;

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

2. LINQ Query Syntax and Method Syntax

LINQ queries can be written using two different syntaxes: query syntax and method syntax. Both syntaxes achieve the same results but offer different approaches to writing queries.

Query Syntax:
        
            namespace LinqExamples;

class Program
{
    static void Main(string[] args)
    {
        var numbers = new List<int> { 1, 2, 3, 4, 5 };

        var evenNumbers = from num in numbers
                          where num % 2 == 0
                          select num;

        foreach (var num in evenNumbers)
        {
            Console.WriteLine(num);
        }
    }
}
        
    
Method Syntax:
        
            namespace LinqExamples;

class Program
{
    static void Main(string[] args)
    {
        var numbers = new List<int> { 1, 2, 3, 4, 5 };

        var evenNumbers = numbers.Where(n => n % 2 == 0);

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

3. Common LINQ Methods

LINQ provides a wide range of methods to perform operations such as filtering, projecting, joining, grouping, and more. Here is a table of some commonly used LINQ methods along with the versions in which they were introduced:

Method Description Version
Where Filters a sequence of values based on a predicate. C# 3.0
Select Projects each element of a sequence into a new form. C# 3.0
SelectMany Projects each element of a sequence to an IEnumerable<T> and flattens the resulting sequences. C# 3.0
OrderBy Sorts the elements of a sequence in ascending order. C# 3.0
OrderByDescending Sorts the elements of a sequence in descending order. C# 3.0
ThenBy Performs a subsequent ordering of the elements in a sequence in ascending order. C# 3.0
ThenByDescending Performs a subsequent ordering of the elements in a sequence in descending order. C# 3.0
GroupBy Groups the elements of a sequence according to a specified key selector function. C# 3.0
ToDictionary Creates a Dictionary<TKey,TValue> from an IEnumerable<T>. C# 3.0
ToList Creates a List<T> from an IEnumerable<T>. C# 3.0
ToArray Creates an array from an IEnumerable<T>. C# 3.0
First Returns the first element of a sequence. C# 3.0
FirstOrDefault Returns the first element of a sequence, or a default value if no element is found. C# 3.0
Last Returns the last element of a sequence. C# 3.0
LastOrDefault Returns the last element of a sequence, or a default value if no element is found. C# 3.0
Single Returns the only element of a sequence, and throws an exception if there is not exactly one element. C# 3.0
SingleOrDefault Returns the only element of a sequence, or a default value if no element is found. C# 3.0
Any Determines whether any element of a sequence satisfies a condition. C# 3.0
All Determines whether all elements of a sequence satisfy a condition. C# 3.0
Count Returns the number of elements in a sequence. C# 3.0
Sum Computes the sum of a sequence of numeric values. C# 3.0
Min Returns the minimum value in a sequence of values. C# 3.0
Max Returns the maximum value in a sequence of values. C# 3.0
Average Computes the average of a sequence of numeric values. C# 3.0
Aggregate Applies an accumulator function over a sequence. C# 3.0
Contains Determines whether a sequence contains a specified element. C# 3.0
Distinct Returns distinct elements from a sequence. C# 3.0
Except Produces the set difference of two sequences. C# 3.0
Intersect Produces the set intersection of two sequences. C# 3.0
Union Produces the set union of two sequences. C# 3.0
Join Correlates the elements of two sequences based on matching keys. C# 3.0
GroupJoin Correlates the elements of two sequences based on key equality and groups the results. C# 3.0
Zip Applies a specified function to the corresponding elements of two sequences. C# 4.0
Skip Bypasses a specified number of elements in a sequence and then returns the remaining elements. C# 3.0
SkipWhile Bypasses elements in a sequence as long as a specified condition is true and then returns the remaining elements. C# 3.0
Take Returns a specified number of contiguous elements from the start of a sequence. C# 3.0
TakeWhile Returns elements from a sequence as long as a specified condition is true, and then skips the remaining elements. C# 3.0
Concat Concatenates two sequences. C# 3.0
DefaultIfEmpty Returns the elements of the specified sequence or the type parameter's default value in a singleton collection if the sequence is empty. C# 3.0
ElementAt Returns the element at a specified index in a sequence. C# 3.0
ElementAtOrDefault Returns the element at a specified index in a sequence or a default value if the index is out of range. C# 3.0
Range Generates a sequence of integral numbers within a specified range. C# 3.0
Repeat Generates a sequence that contains one repeated value. C# 3.0
Empty Returns an empty IEnumerable<T> that has the specified type argument. C# 3.0
AsEnumerable Returns the input typed as IEnumerable<T>. C# 3.0
AsQueryable Converts an IEnumerable to an IQueryable. C# 3.0
Cast Casts the elements of an IEnumerable to the specified type. C# 3.0
OfType Filters the elements of an IEnumerable based on a specified type. C# 3.0
Append Appends a value to the end of the sequence. C# 8.0
Prepend Prepends a value to the beginning of the sequence. C# 8.0
ToHashSet Creates a HashSet<T> from an IEnumerable<T>. C# 8.0
Chunk Splits the elements of a sequence into chunks of size at most chunkSize. C# 10.0
IntersectBy Produces the set intersection of two sequences according to a specified key selector. C# 10.0
UnionBy Produces the set union of two sequences according to a specified key selector. C# 10.0
ExceptBy Produces the set difference of two sequences according to a specified key selector. C# 10.0
MaxBy Returns the maximum value in a sequence according to a specified key selector. C# 10.0
MinBy Returns the minimum value in a sequence according to a specified key selector. C# 10.0

4. Advanced LINQ Concepts

Apart from the basic operations, LINQ also provides support for more advanced querying techniques such as joining, grouping, and working with complex data structures.

Join:
        
            namespace LinqExamples;

class Program
{
    public class Student
    {
        public int StudentId { get; set; }
        public string Name { get; set; }
    }

    public class Score
    {
        public int StudentId { get; set; }
        public int ScoreValue { get; set; }
    }

    static void Main(string[] args)
    {
        var students = new List<Student>
        {
            new Student { StudentId = 1, Name = "John" },
            new Student { StudentId = 2, Name = "Jane" }
        };

        var scores = new List<Score>
        {
            new Score { StudentId = 1, ScoreValue = 90 },
            new Score { StudentId = 2, ScoreValue = 85 }
        };

        var studentScores = from student in students
                            join score in scores on student.StudentId equals score.StudentId
                            select new { student.Name, score.ScoreValue };

        foreach (var studentScore in studentScores)
        {
            Console.WriteLine($"{studentScore.Name}: {studentScore.ScoreValue}");
        }
    }
}
        
    
GroupBy:
        
            namespace LinqExamples;

class Program
{
    static void Main(string[] args)
    {
        var numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        var groupedNumbers = from num in numbers
                             group num by num % 3 into numGroup
                             select new { Remainder = numGroup.Key, Numbers = numGroup.ToList() };

        foreach (var group in groupedNumbers)
        {
            Console.WriteLine($"Numbers with remainder {group.Remainder} when divided by 3:");
            foreach (var num in group.Numbers)
            {
                Console.WriteLine(num);
            }
        }
    }
}
        
    
Complex Data Structures:
        
            namespace LinqExamples;

class Program
{
    public class Student
    {
        public int StudentId { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }

    static void Main(string[] args)
    {
        var students = new List<Student>
        {
            new Student { StudentId = 1, Name = "John", Age = 18 },
            new Student { StudentId = 2, Name = "Jane", Age = 20 },
            new Student { StudentId = 3, Name = "Jake", Age = 18 }
        };

        var studentGroups = from student in students
                            group student by student.Age into ageGroup
                            select new { Age = ageGroup.Key, Students = ageGroup.ToList() };

        foreach (var group in studentGroups)
        {
            Console.WriteLine($"Students aged {group.Age}:");
            foreach (var student in group.Students)
            {
                Console.WriteLine(student.Name);
            }
        }
    }
}
        
    

5. LINQ in Practice

Let's look at a practical example of using LINQ to query a collection of objects. Suppose we have a list of students and we want to filter, sort, and project this data.

Practical Example:
        
            namespace LinqExamples;

class Program
{
    public class Student
    {
        public int StudentId { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
        public int Grade { get; set; }
    }

    static void Main(string[] args)
    {
        var students = new List<Student>
        {
            new Student { StudentId = 1, Name = "John", Age = 18, Grade = 90 },
            new Student { StudentId = 2, Name = "Jane", Age = 20, Grade = 85 },
            new Student { StudentId = 3, Name = "Jake", Age = 18, Grade = 95 }
        };

        var topStudents = students
                          .Where(s => s.Grade > 85)
                          .OrderBy(s => s.Name)
                          .Select(s => new { s.Name, s.Grade });

        foreach (var student in topStudents)
        {
            Console.WriteLine($"{student.Name}: {student.Grade}");
        }
    }
}
        
    


6. Conclusion

LINQ is a versatile and powerful tool in C# that simplifies data querying and manipulation. By integrating querying capabilities directly into the language, LINQ provides a consistent and readable approach to working with different types of data sources.