Cross Join, Group Join (the final core LINQ entry
Believe it or not, this is the last entry on the core LINQ samples. (Of course, that still leaves LINQ to SQL and LINQ to XML (the technologies formerly known as DLinq and XLinq).
This entry covers the Join extension method and keywords. ‘join’ does a simple Cross Join between two disparate sequences. For example, this method builds a list of products and categories based on a list of chosen categories:
public void Linq102() {
string[] categories = new string[]{
"Beverages",
"Condiments",
"Vegetables",
"Dairy Products",
"Seafood" }; List<Product> products = GetProductList();
var q =
from c in categories
join p in products on c equals p.Category
select new { Category = c, p.ProductName };
foreach (var v in q) {
Console.WriteLine(v.ProductName + ": " + v.Category);
}
}The results show the product name and the category:
Chai: Beverages
Chang: Beverages
…
Aniseed Syrup: Condiments
Chef Anton's Cajun Seasoning: Condiments
Chef Anton's Gumbo Mix: Condiments
…
Queso Cabrales: Dairy Products
Queso Manchego La Pastora: Dairy Products
…
Ikura: Seafood
Carnarvon Tigers: Seafood
Röd Kaviar: Seafood
That’s useful as far as it goes, but chances are you’d prefer having the products grouped. That’s a simple matter of using a projection to create a new sequence of categories, each with a sequence of products. That’s a group join:
public void Linq103() {
string[] categories = new string[]{
"Beverages",
"Condiments",
"Vegetables",
"Dairy Products",
"Seafood" };
List<Product> products = GetProductList(); var q =
from c in categories
join p in products on c equals p.Category into ps // note grouping
select new { Category = c, Products = ps };
foreach (var v in q) { // q is a list of categories
Console.WriteLine(v.Category + ":");
foreach (var p in v.Products) { // that contains products
Console.WriteLine(" " + p.ProductName);
}
}
}The (abbreviated) results are:
Beverages:
Chai
Chang
…
Condiments:
Aniseed Syrup
Chef Anton's Cajun Seasoning
…
Vegetables:
Dairy Products:
Queso Cabrales
Queso Manchego La Pastora
Gorgonzola Telino
…
Seafood:
Ikura
Konbu
…
Notice that there are no vegetables. I’ll come back to that in a minute. But first, suppose you want to combine the cross join with a group join to create a single sequence. It’s a little more verbose, and the results are the same as the first query:
public void Linq104() {
string[] categories = new string[]{
"Beverages",
"Condiments",
"Vegetables",
"Dairy Products",
"Seafood" }; List<Product> products = GetProductList();
var q =
from c in categories
join p in products on c equals p.Category into ps
from p in ps
select new { Category = c, p.ProductName };
foreach (var v in q) {
Console.WriteLine(v.ProductName + ": " + v.Category);
}
}Abbreviated results:
Chai: Beverages
Chang: Beverages
…
Chef Anton's Gumbo Mix: Condiments
Grandma's Boysenberry Spread: Condiments
Northwoods Cranberry Sauce: Condiments
…
Queso Manchego La Pastora: Dairy Products
Gorgonzola Telino: Dairy Products
Mascarpone Fabioli: Dairy Products
Geitost: Dairy Products
…
Ikura: Seafood
Konbu: Seafood
Carnarvon Tigers: Seafood
…
And finally, you can use LINQ to create an outer join. Note that here, all the left side elements are included at least once, even if there are no matching elements in the right side. That’s the “No Products” entry for vegetables. The code works similar to the earlier examples, but note the second from clause. If ps is empty, the from clause returns null. Then, the select clause adds “No Products” instead of p.ProductName.
public void Linq105() {
string[] categories = new string[]{
"Beverages",
"Condiments",
"Vegetables",
"Dairy Products",
"Seafood" };
List<Product> products = GetProductList(); var q =
from c in categories
join p in products on c equals p.Category into ps
from p in ps.DefaultIfEmpty()
select new { Category = c,
ProductName = p == null ? "(No products)" : p.ProductName };
foreach (var v in q) {
Console.WriteLine(v.ProductName + ": " + v.Category);
}
}The results are:
Chai: Beverages
Chang: Beverages
…
Aniseed Syrup: Condiments
Chef Anton's Cajun Seasoning: Condiments
…
(No products): Vegetables
Queso Cabrales: Dairy Products
Queso Manchego La Pastora: Dairy Products
…
Ikura: Seafood
Konbu: Seafood
I’m not sure that Join is as important as it has been in the past. The select and projection operations can solve many of the same problems. But, Join has become a common operation, so it’s good to know it’s still there.
Next, we’ll start examining DLINQ and XLINQ.
Part 1
The general Query Syntax
Part 2
The one where I discuss Object and Collection Initializers
Part 3
The one where I finish restriction operators
Part 4
Begining to discuss projections
Part 5
Anonymous types and projections
Part 6
Discussing indexed, filtered, and compound queries
Part 7
Finishing up the projection items
Part 8
Projection operators and extension methods
Part 9
OrderBy, ThenBy, and Descending, oh my
Part 10
Grouping operators and building nested groups
Part 11
Set Operations, you bet
Part 12
Conversions: caching collections
Part 13
Where U at item, where U At?
Part 14
Boolean tests on sequences
Part 15
Aggregation operators: Sum, Product, Averages, and more
Part 16
Concatenation and EqualAll
Part 17
Do it now, or do it later