Search This Blog

Tuesday, August 21, 2018

LINQ (Language Integrated Query) - PART - 2


Topics Included:
#GroupBy   #FirstOrDefault  #LastOrDefault  #ElementAtOrDefault  #SingleOrDefault  #DefaultIfEmpty  #GroupJoin  #INNERJOIN  #LEFTOUTERJOIN  #Distinct  #Intersect  #Except  #Range  #Repeat  #Empty  #Concat  #SequenceEqual  #All  #ANY  #Contains 



#GroupBy


GroupBy operator belong to Grouping Operators category. This operator takes a flat sequence of items, organize that sequence into groups (IGrouping<K,V>) based on a specific key and return groups of sequences.

In short, GroupBy creates and returns a sequence of IGrouping<K,V>

E.G. 1)
var employeeGroup = from employee in Employee.GetAllEmployees()
                    group employee by employee.Department;

foreach (var group in employeeGroup)
{
    Console.WriteLine("{0} - {1}", group.Key, group.Count());
}


E.G. 2)
var employeeGroup = from employee in Employee.GetAllEmployees()
                                      group employee by employee.Department;

foreach (var group in employeeGroup)
{
    Console.WriteLine("{0} - {1}", group.Key, group.Count());
    Console.WriteLine("----------");
    foreach (var employee in group)
    {
        Console.WriteLine(employee.Name + "\t" + employee.Department);
    }
    Console.WriteLine(); Console.WriteLine();
}

E.G. 3)
var employeeGroup = from employee in Employee.GetAllEmployees()
                                      group employee by employee.Department into eGroup
                                      orderby eGroup.Key
                                      select new
                                      {
                                           Key = eGroup.Key,
                                           Employees = eGroup.OrderBy(x => x.Name)
                                      };

foreach (var group in employeeGroup)
{
    Console.WriteLine("{0} - {1}", group.Key, group.Employees.Count());
    Console.WriteLine("----------");
    foreach (var employee in group.Employees)
    {
        Console.WriteLine(employee.Name + "\t" + employee.Department);
    }
    Console.WriteLine(); Console.WriteLine();
}

Group by multiple keys

roup employees by Department and then by Gender. The employee groups should be sorted first by Department and then by Gender in ascending order. Also, employees within each group must be sorted in ascending order by Name.


var employeeGroups = Employee.GetAllEmployees()
.GroupBy(x => new { x.Department, x.Gender })
.OrderBy(g => g.Key.Department).ThenBy(g => g.Key.Gender)
.Select(g => new
{
    Dept = g.Key.Department,
    Gender = g.Key.Gender,
    Employees = g.OrderBy(x => x.Name)
});



Element Operators

Element Operators retrieve a single element from a sequence using the element index or based on a condition. All of these methods have a corresponding overloaded version that accepts a predicate.

#First

There are 2 overloaded versions of this method. The first overloaded version that does not have any parameters simply returns the first element of a sequence.

E.G. 1: Returns the first element from the sequence
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int result = numbers.First();
Console.WriteLine("Result = " + result);

Output:
Result = 1

If the sequence does not contain any elements, then First() method throws an InvalidOperationException.

E.G. 2: Throws InvalidOperationException.
int[] numbers = { };
int result = numbers.First();
Console.WriteLine("Result = " + result);

Output:
Unhandled Exception: System.InvalidOperationException: Sequence contains no elements

The second overloaded version is used to find the first element in a sequence based on a condition. If the sequence does not contain any elements or if no element in the sequence satisfies the condition then an InvalidOperationException is thrown.

E.G. 3: Returns the first even number from the sequence
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int result = numbers.First(x => x % 2 == 0);
Console.WriteLine("Result = " + result);

Output:
Result = 2

E.G. 4: Throws InvalidOperationException, as no element in the sequence satisfies the condition specified by the predicate.
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int result = numbers.First(x => x % 2 == 100);
Console.WriteLine("Result = " + result);

Output:
Unhandled Exception: System.InvalidOperationException: Sequence contains no matching element

#FirstOrDefault

This is very similar to First, except that this method does not throw an exception when there are no elements in the sequence or when no element satisfies the condition specified by the predicate. Instead, a default value of the type that is expected is returned. For reference types the default is NULL and for value types the default depends on the actual type expected.

E.G. 5: Returns ZERO. No element in the sequence satisfies the condition, so the default value (ZERO) for int is returned.
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int result = numbers.FirstOrDefault(x => x % 2 == 100);
Console.WriteLine("Result = " + result);

Last : Very similar to First, except it returns the last element of the sequence.

#LastOrDefault

Very similar to FirstOrDefault, except it returns the last element of the sequence.

ElementAt : Returns an element at a specified index. If the sequence is empty or if the provided index value is out of range, then an ArgumentOutOfRangeException is thrown.

E.G. 6: Returns element from the sequence that is at index position 1.
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int result = numbers.ElementAt(1);
Console.WriteLine("Result = " + result);

Output:
Result = 2

E.G. 7: Throws ArgumentOutOfRangeException
int[] numbers = { };
int result = numbers.ElementAt(0);
Console.WriteLine("Result = " + result);

Output:
Unhandled Exception: System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.

#ElementAtOrDefault

Similar to ElementAt except that this method does not throw an exception, if the sequence is empty or if the provided index value is out of range. Instead, a default value of the type that is expected is returned.

#Single

There are 2 overloaded versions of this method. The first overloaded version that does not have any parameters returns the only element of the sequence.

E.G. 8: Returns the only element (1) of the sequence.
int[] numbers = { 1 };
int result = numbers.Single();
Console.WriteLine("Result = " + result);

Output:
Result = 1

Single() method throws an exception if the sequence is empty or has more than one element.

E.G. 9: Throws InvalidOperationException as the sequence contains more than ONE element.
int[] numbers = { 1, 2 };
int result = numbers.Single();
Console.WriteLine("Result = " + result);

Output:
Unhandled Exception: System.InvalidOperationException: Sequence contains more than one element

The second overloaded version of the Single() method is used to find the only element in a sequence that satisfies a given condition. An exception will be thrown if any of the following is true
a) If the sequence does not contain any elements OR
b) If no element in the sequence satisfies the condition OR
c) If more than one element in the sequence satisfies the condition

E.G. 10: Throws InvalidOperationException as more than one element in the sequence satisfies the condition
int[] numbers = { 1, 2, 4 };
int result = numbers.Single(x => x % 2 == 0);
Console.WriteLine("Result = " + result);

Output:
Unhandled Exception: System.InvalidOperationException: Sequence contains more than one matching element

#SingleOrDefault

Very similar to Single(), except this method does not throw an exception when the sequence is empty or when no element in the sequence satisfies the given condition. Just like Single(), this method will still throw an exception, if more than one element in the sequence satisfies the given condition.

E.G. 11: Throws InvalidOperationException as more than one element in the sequence satisfies the given condition
int[] numbers = { 1, 2, 4 };
int result = numbers.SingleOrDefault(x => x % 2 == 0);
Console.WriteLine("Result = " + result);

Output:
Unhandled Exception: System.InvalidOperationException: Sequence contains more than one matching element

#DefaultIfEmpty

If the sequence on which this method is called is not empty, then the values of the original sequence are returned.

E.G. 12: Returns a copy of the original sequence
int[] numbers = { 1, 2, 3 };
IEnumerable<int> result = numbers.DefaultIfEmpty();
foreach (int i in result)
{
    Console.WriteLine(i);
}

Output:
1
2
3

If the sequence is empty, then DefaultIfEmpty() returns a sequence with the default value of the expected type.

E.G. 13: Since the sequence is empty, a sequence containing the default value (ZERO) of int is returned.
int[] numbers = { };
IEnumerable<int> result = numbers.DefaultIfEmpty();
foreach (int i in result)
{
    Console.WriteLine(i);
}

Output:
0

The other overloaded version with a parameter allows us to specify a default value. If this method is called on a sequence that is not empty, then the values of the original sequence are returned. If the sequence is empty, then this method returns a sequence with the specified defualt value.

E.G. 14: Since the sequence is empty, a sequence containing the specified default value (10) is returned.
int[] numbers = { };
IEnumerable<int> result = numbers.DefaultIfEmpty(10);
foreach (int i in result)
{
    Console.WriteLine(i);
}

Output:
10



#GroupJoin


Group Join produces hierarchical data structures. Each element from the first collection is paired with a set of correlated elements from the second collection.

public class Department
{
    public int ID { get; set; }
    public string Name { get; set; }

    public static List<Department> GetAllDepartments()
    {
        return new List<Department>()
        {
            new Department { ID = 1, Name = "IT"},
            new Department { ID = 2, Name = "HR"},
            new Department { ID = 3, Name = "Payroll"},
        };
    }
}

public class Employee
{
    public int ID { get; set; }
    public string Name { get; set; }
    public int DepartmentID { get; set; }

    public static List<Employee> GetAllEmployees()
    {
        return new List<Employee>()
        {
            new Employee { ID = 1, Name = "Mark", DepartmentID = 1 },
            new Employee { ID = 2, Name = "Steve", DepartmentID = 2 },
            new Employee { ID = 3, Name = "Ben", DepartmentID = 1 },
            new Employee { ID = 4, Name = "Philip", DepartmentID = 1 },
            new Employee { ID = 5, Name = "Mary", DepartmentID = 2 },
            new Employee { ID = 6, Name = "Valarie", DepartmentID = 2 },
            new Employee { ID = 7, Name = "John", DepartmentID = 1 },
            new Employee { ID = 8, Name = "Pam", DepartmentID = 1 },
            new Employee { ID = 9, Name = "Stacey", DepartmentID = 2 },
            new Employee { ID = 10, Name = "Andy", DepartmentID = 1}
        };
    }
}

E.G. 1: Group employees by Department.
var employeesByDepartment = Department.GetAllDepartments()
   .GroupJoin(Employee.GetAllEmployees(),
     d => d.ID,
     e => e.DepartmentID,
     (department, employees) => new
     {
         Department = department,
         Employees = employees
     });

foreach (var department in employeesByDepartment)
{
    Console.WriteLine(department.Department.Name);
    foreach (var employee in department.Employees)
    {
        Console.WriteLine(" " + employee.Name);
    }
    Console.WriteLine();
}

E.G. 2: Rewrite Example 1 using SQL Like Query syntax.
var employeesByDepartment = from d in Department.GetAllDepartments()
           join e in Employee.GetAllEmployees()
           on d.ID equals e.DepartmentID into eGroup
           select new
           {
              Department = d,
              Employees = eGroup
           };

Note: Group Join uses the join operator and the into keyword to group the results of the join.

#INNER JOIN 


If you have 2 collections, and when you perform an inner join, then only the matching elements between the 2 collections are included in the result set. Non - Matching elements are excluded from the result set.

E.G. 1:
 var result = Employee.GetAllEmployees().Join(Department.GetAllDepartments(),
            e => e.DepartmentID,
            d => d.ID, (employee, department) => new
            {
                EmployeeName = employee.Name,
                DepartmentName = department.Name
            });


E.G. 2: Rewrite Example 1 using SQL Like Query syntax.
var result = from e in Employee.GetAllEmployees()
        join d in Department.GetAllDepartments()
        on e.DepartmentID equals d.ID
        select new
        {
            EmployeeName = e.Name,
            DepartmentName = d.Name
        };

#LEFT OUTER JOIN


With INNER JOIN only the matching elements are included in the result set. Non-matching elements are excluded from the result set.

With LEFT OUTER JOIN all the matching elements + all the non matching elements from the left collection are included in the result set

Use DefaultIfEmpty() method on the results of a group join to implement Left Outer Join

E.G. 1: Implement a Left Outer Join between Employees and Department collections and print all the Employees and their respective department names. Employees without a department, should display "No Department" against their name.
var result = from e in Employee.GetAllEmployees()
                    join d in Department.GetAllDepartments()
                    on e.DepartmentID equals d.ID into eGroup
                    from d in eGroup.DefaultIfEmpty()
                    select new
                    {
                         EmployeeName = e.Name,
                         DepartmentName = d == null ? "No Department" : d.Name
                    };


E.G. 2: Rewrite Example 1 using extension method syntax.
var result = Employee.GetAllEmployees()
                        .GroupJoin(Department.GetAllDepartments(),
                                e => e.DepartmentID,
                                d => d.ID,
                                (emp, depts) => new { emp, depts })
                        .SelectMany(z => z.depts.DefaultIfEmpty(),
                                (a, b) => new
                                {
                                        EmployeeName = a.emp.Name,
                                        DepartmentName = b == null ? "No Department" : b.Name
                                });

To implement Left Outer Join, with extension method syntax we use the GroupJoin() method along with SelectMany() and DefaultIfEmpty() methods.

#Distinct


var result = countries.Distinct();
var result = countries.Distinct(StringComparer.OrdinalIgnoreCase);

#Union


var result = numbers1.Union(numbers2);

#Intersect


var result = numbers1.Intersect(numbers2);

#Except


var result = numbers1.Except(numbers2);



Generation Operators

#Range


var evenNumbers = Enumerable.Range(1, 10).Where(x => x % 2 == 0);

#Repeat


var result = Enumerable.Repeat("Hello", 5);

#Empty


IEnumerable<int> result = GetIntegerSequence() ?? Enumerable.Empty<int>();

Here we are using NULL-COALESCING operator that checks if the GetIntegerSequence() method returns NULL, in which case the result variable is initialized with an empty IEnumerable<int>.



#Concat


var result = numbers1.Concat(numbers2);

Difference between Concat and Union operators

Concat operator combines 2 sequences into 1 sequence. Duplicate elements are not removed. It simply returns the items from the first sequence followed by the items from the second sequence.

Union operator also combines 2 sequences into 1 sequence, but will remove the duplicate elements.

#SequenceEqual


SequenceEqual() method is used to determine whether two sequences are equal. This method returns true if the sequences are equal otherwise false.

For 2 sequences to be equal
1. Both the sequences should have same number of elements and
2. Same values should be present in the same order in both the sequences

E.G. 1: SequenceEqual() returns true.

string[] countries1 = { "USA", "India", "UK" };
string[] countries2 = { "USA", "India", "UK" };

var result = countries1.SequenceEqual(countries2);

E.G. 2: In this case, SequenceEqual() returns false, as the default comparison is case sensitive.

string[] countries1 = { "USA", "INDIA", "UK" };
string[] countries2 = { "usa", "india", "uk" };

var result = countries1.SequenceEqual(countries2);

Console.WriteLine("Are Equal = " + result);

E.G. 3: If we want the comparison to be case insensitive, then use the other overloaded version of SequenceEqual() method to which we can pass an alternate comparer.

string[] countries1 = { "USA", "INDIA", "UK" };
string[] countries2 = { "usa", "india", "uk" };

var result = countries1.SequenceEqual(countries2, StringComparer.OrdinalIgnoreCase);


E.G. 4: SequenceEqual() returns false. This is because, although both the sequences contain same data, the data is not present in the same order.

string[] countries1 = { "USA", "INDIA", "UK" };
string[] countries2 = { "UK", "INDIA", "USA" };

var result = countries1.SequenceEqual(countries2);

Console.WriteLine("Are Equal = " + result);

E.G. 5: To fix the problem in Example 4, use OrderBy() to sort data in the source sequences.

string[] countries1 = { "USA", "INDIA", "UK" };
string[] countries2 = { "UK", "INDIA", "USA" };

var result = countries1.OrderBy(c =] c).SequenceEqual(countries2.OrderBy(c =] c));



Quantifiers 


#All()

method returns true if all the elements in a sequence satisfy a given condition, otherwise false.

Example 1 : Returns true, as all the numbers are less than 10

int[] numbers = { 1,2,3,4,5 };

var result = numbers.All(x =] x [ 10);

#ANY()


There are 2 overloaded versions of Any() method. The version without any parameters checks if the sequence contains at least one element. The other version with a predicate parameter checks if the sequence contains at least one element that satisfies a given condition.

int[] numbers = { 1,2,3,4,5 };

var result = numbers.Any();

#Contains()


There are 2 overloaded versions of the Contains() method. One of the overloaded version checks if the sequence contains a specified element using the default equality comparer. The other overloaded version checks if the sequence contains a specified element using an alternate equality comparer.

Returns true as the sequence contains number 3. In this case the default equality comparer is used.

int[] numbers = { 1,2,3,4,5 };

var result = numbers.Contains(3);




LINQ PAD Download

https://www.linqpad.net/Download.aspx




No comments:

Post a Comment


This is a User Friendly Blog.
Simple Interface and Simple Controls are used.
Post your comments so i can modify blog regarding your wish.