Back to: LINQ Tutorial For Beginners and Professionals
LINQ Group Join in C# with Examples
In this article, I will discuss How to Implement LINQ Group Join in C# using Method and Query Syntax with Examples. Please read our previous article discussing How to Join Multiple Data Sources in LINQ with Examples.
What is LINQ Group Join?
In LINQ (Language Integrated Query), the GroupJoin method correlates the elements of two sequences based on matching keys and groups the results. It is particularly used to create hierarchical data structures, where each element from the first sequence (outer sequence) is mapped to a collection of related elements from the second sequence (inner sequence). If an element from the outer sequence has no matches in the inner sequence, it is still included in the result with an empty collection of inner elements.
It’s similar to a SQL Group Join, where one set of items is matched with a collection of related items from another set. There are two overloaded versions of this GroupJoin method available in LINQ. They are as follows.
The only difference between the two overloaded versions is that the second version takes an additional IEqualityComparer as an extra parameter. So, while working with LINQ Group Join, we need to understand the following things.
- Joining Collections: It starts with two collections. One is considered the ‘outer’ sequence, and the other is the ‘inner’ sequence.
- Key Selector: You specify a key for each item in both sequences. These keys are used to match items from the outer sequence to items from the inner sequence.
- Grouping: The result of a Group Join is a sequence of objects, where each object consists of a single item from the outer collection and a collection of all matching items from the inner collection. This is different from a regular join, which would result in a flat list of matched pairs.
- Hierarchy Creation: This grouping effectively creates a hierarchical relationship. For example, you could group a list of departments (outer) with a list of employees (inner), where each department object is paired with a collection of employees who work in that department.
Syntax to Use LINQ Group Join:
var groupJoinQuery = outerSequence.GroupJoin( innerSequence, outerKeySelector, innerKeySelector, (outerElement, innerElements) => new { Outer = outerElement, Inners = innerElements });
Here is the explanation of the above syntax:
- outerSequence: The first sequence to join.
- innerSequence: The sequence to join to the first sequence.
- outerKeySelector: A function to extract the join key from each element of the first sequence.
- innerKeySelector: A function to extract the join key from each element of the second sequence.
- resultSelector: A function to create a result element from an element from the first sequence and a collection of matching elements from the second sequence.
Examples to Understand LINQ Group Join in C#:
Let us understand How to Implement LINQ Group Join with examples using C# language. For this, we will use the following two model classes, i.e., Employee and Department. So, create a class file named Employee.cs and copy and paste the following code. This class has 3 properties, i.e., ID, Name, and DepartmentId. We have also created one method to return a collection of Employees, which will be our first data source for the Group Join.
using System.Collections.Generic; namespace LINQGroupJoin { 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 = "Preety", DepartmentId = 10}, new Employee { ID = 2, Name = "Priyanka", DepartmentId =20}, new Employee { ID = 3, Name = "Anurag", DepartmentId = 30}, new Employee { ID = 4, Name = "Pranaya", DepartmentId = 30}, new Employee { ID = 5, Name = "Hina", DepartmentId = 20}, new Employee { ID = 6, Name = "Sambit", DepartmentId = 10}, new Employee { ID = 7, Name = "Happy", DepartmentId = 10}, new Employee { ID = 8, Name = "Tarun", DepartmentId = 0}, new Employee { ID = 9, Name = "Santosh", DepartmentId = 10}, new Employee { ID = 10, Name = "Raja", DepartmentId = 20}, new Employee { ID = 11, Name = "Ramesh", DepartmentId = 30} }; } } }
Next, create another class file named Department.cs and copy and paste the following code. This class has 2 properties, i.e., ID and Name. We have also created one method to return a collection of departments, which will be our second data source for the Group Join.
using System.Collections.Generic; namespace LINQGroupJoin { 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 = 10, Name = "IT"}, new Department { ID = 20, Name = "HR"}, new Department { ID = 30, Name = "Sales" }, }; } } }
As you can see, we created the above Employee and Department classes with some simple properties. The common property is Department ID, i.e., the ID property in the Department class and DepartmentID property in the Employee class. Then, we create two simple methods to return the respective data sources. Further, if you notice, the employee with ID 8 does not have a department.
Example to Understand LINQ GroupJoin Method Using Method Syntax in C#
Now, our business requirement is to group the employees by department. So, the outer data source will be the department data source, and the inner data source will be the employee data source. We must use the LINQ GroupJoin Method to group the employees by department. The following code snippet shows how to group the employees by department using the LINQ GroupJoin Method using Method Syntax.
As you can see in the above code snippet, our Outer Data Source is the Department’s collection, and our Inner Data Source is the Employee’s collection. Here, we are accessing the Department collection using the dept variable and the Employees collection using the emp variable. Here, the Outer Key Selector is the ID property of the Department class, and the Inner Key Selector is the DepartmentId property of the Employee class. Finally, we are projecting the dept and emp to an Anonymous type. The complete example code is given below. The following example code is self-explained, so please go through the comment lines.
using System.Linq; using System; namespace LINQGroupJoin { class Program { static void Main(string[] args) { //Group Employees by Department using Method Syntax var GroupJoinMS = Department.GetAllDepartments(). //Outer Data Source i.e. Departments GroupJoin( //Performing Group Join with Inner Data Source Employee.GetAllEmployees(), //Inner Data Source dept => dept.ID, //Outer Key Selector i.e. the Common Property emp => emp.DepartmentId, //Inner Key Selector i.e. the Common Property (dept, emp) => new { dept, emp } //Projecting the Result to an Anonymous Type ); //Printing the Result set //Outer Foreach is for Each department foreach (var item in GroupJoinMS) { Console.WriteLine("Department :" + item.dept.Name); //Inner Foreach loop for each employee of a Particular department foreach (var employee in item.emp) { Console.WriteLine(" EmployeeID : " + employee.ID + " , Name : " + employee.Name); } } Console.ReadLine(); } } }
Output:
As you can see, the employee with ID 8 does not display here. This is because the employee with ID 8 does not belong to any department.
Example to Understand LINQ GroupJoin Using Query Syntax in C#
In LINQ Query Syntax, there is no such Group Join operator available. Here, we need to use the LINQ Inner Join and the “into” operator. For a better understanding, please look at the following code snippet. Here, we have divided the code snippet into three sections for a better understanding. In the first section, we perform the LINQ Inner Join Operation between the Department and Employee Data Sources. In the second section, we project the result of the Inner Join into a variable called EmployeeGroups using the “into” operator. And in the final section, we are projecting the final result set as dept and EmployeeGroups. That is, it will group employees by department.
The complete example code is given below. The following example code is self-explained, so please go through the comment lines. Here, we are using LINQ Query Syntax to implement Group Join.
using System.Linq; using System; namespace LINQGroupJoin { class Program { static void Main(string[] args) { //Group Employees by Department using Query Syntax var GroupJoinQS = from dept in Department.GetAllDepartments() //Outer Data Source i.e. Departments join emp in Employee.GetAllEmployees() //Joining with Inner Data Source i.e. Employees on dept.ID equals emp.DepartmentId //Joining Condition into EmployeeGroups //Projecting the Joining Result into EmployeeGroups //Final Result include each department and the corresponding employees select new { dept, EmployeeGroups }; //Printing the Result set //Outer Foreach is for Each department foreach (var item in GroupJoinQS) { Console.WriteLine("Department :" + item.dept.Name); //Inner Foreach loop for each employee of a Particular department foreach (var employee in item.EmployeeGroups) { Console.WriteLine(" EmployeeID : " + employee.ID + " , Name : " + employee.Name); } } Console.ReadLine(); } } }
Run the application and print the same output as the previous example, as shown in the image below. You can see the employee with ID 8 does not display here. This is because the employee with ID 8 does not belong to any department.
Projecting the Result to a Named Type:
So far, in the examples we have discussed, we have projected the result to an anonymous type. Can we Project the Result to a Named type? Yes, it is also possible to project the result to a named type instead of an anonymous type. Let us see how we can do this. First, create a class file named GroupEmployeeByDepartment.cs with the required properties you want in the result set. We have created the class with the following two properties per our requirements.
using System.Collections.Generic; namespace LINQGroupJoin { class GroupEmployeeByDepartment { public Department Department { get; set; } public List<Employee> Employees { get; set; } } }
Next, modify the Main method of the Program class as follows. Here, you can see we are projecting the result to the above-created GroupEmployeeByDepartment type.
using System.Linq; using System; namespace LINQGroupJoin { class Program { static void Main(string[] args) { //Group Employees by Department using Method Syntax var GroupJoinMS = Department.GetAllDepartments(). //Outer Data Source i.e. Departments GroupJoin( //Performing Group Join with Inner Data Source Employee.GetAllEmployees(), //Inner Data Source dept => dept.ID, //Outer Key Selector i.e. the Common Property emp => emp.DepartmentId, //Inner Key Selector i.e. the Common Property //Projecting the Result to a Named Type (dept, emp) => new GroupEmployeeByDepartment { Department = dept, Employees = emp.ToList() } ); //Group Employees by Department using Query Syntax var GroupJoinQS = from dept in Department.GetAllDepartments() //Outer Data Source i.e. Departments join emp in Employee.GetAllEmployees() //Joining with Inner Data Source i.e. Employees on dept.ID equals emp.DepartmentId //Joining Condition into EmployeeGroups //Projecting the Joining Result into EmployeeGroups //Projecting the Result to a Named Type select new GroupEmployeeByDepartment { Department = dept, Employees = EmployeeGroups.ToList() }; foreach (var item in GroupJoinQS) { Console.WriteLine("Department :" + item.Department.Name); foreach (var employee in item.Employees) { Console.WriteLine(" EmployeeID : " + employee.ID + " , Name : " + employee.Name); } } Console.ReadLine(); } } }
With the above changes in place, run the application code, and it will give you the following output.
Projecting to Anonymous Type with User-Defined Property Names in ResultSet:
It is also possible to specify user-defined names in the result set. For a better understanding, please have a look at the following example. In the below example, we are projecting the result to an anonymous type with user-defined property names. The following example is the same as the previous example, except we are projecting to an anonymous type.
using System.Linq; using System; namespace LINQGroupJoin { class Program { static void Main(string[] args) { //Group Employees by Department using Method Syntax var GroupJoinMS = Department.GetAllDepartments(). //Outer Data Source i.e. Departments GroupJoin( //Performing Group Join with Inner Data Source Employee.GetAllEmployees(), //Inner Data Source dept => dept.ID, //Outer Key Selector i.e. the Common Property emp => emp.DepartmentId, //Inner Key Selector i.e. the Common Property //Projecting the Result with User Defined Names (dept, emp) => new { Department = dept, Employees = emp.ToList() } ); //Group Employees by Department using Query Syntax var GroupJoinQS = from dept in Department.GetAllDepartments() //Outer Data Source i.e. Departments join emp in Employee.GetAllEmployees() //Joining with Inner Data Source i.e. Employees on dept.ID equals emp.DepartmentId //Joining Condition into EmployeeGroups //Projecting the Joining Result into EmployeeGroups //Projecting the Result with User Defined Names select new { Department = dept, Employees = EmployeeGroups.ToList() }; foreach (var item in GroupJoinQS) { Console.WriteLine("Department :" + item.Department.Name); foreach (var employee in item.Employees) { Console.WriteLine(" EmployeeID : " + employee.ID + " , Name : " + employee.Name); } } Console.ReadLine(); } } }
Output:
When to Use LINQ Group Join in C#?
The LINQ GroupJoin method is particularly useful in scenarios where you need to create a one-to-many relationship between two sequences. Here are some common situations where GroupJoin is beneficial:
- Hierarchical Data Representation: When you need to represent hierarchical or nested data, such as categories and their associated products, employees and their departments, or authors and their books.
- Data Aggregation: In scenarios where you want to aggregate data from related collections, like summing sales by region or calculating average scores by class.
- Combining Data from Different Sources: When dealing with data from different sources (like databases, XML, or JSON), you need to combine related items based on a common key.
- One-to-Many Relationships: Particularly useful in representing one-to-many relationships, where each element in the first collection (outer sequence) is mapped to a collection of related elements from the second collection (inner sequence).
- Reporting and Data Analysis: For generating reports or performing data analysis, where you need to group related data together, such as listing all orders for each customer.
In the next article, I will discuss the LINQ Left Outer Join in C# with Examples. In this article, I explain the LINQ GroupJoin using Method and Query Syntax Examples in C#. I hope you enjoy this LINQ GroupJoin Method in C# with Examples article.
About the Author: Pranaya Rout
Pranaya Rout has published more than 3,000 articles in his 11-year career. Pranaya Rout has very good experience with Microsoft Technologies, Including C#, VB, ASP.NET MVC, ASP.NET Web API, EF, EF Core, ADO.NET, LINQ, SQL Server, MYSQL, Oracle, ASP.NET Core, Cloud Computing, Microservices, Design Patterns and still learning new technologies.