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
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.