Hooked on LINQ

Hooked on LINQ - Developers' Wiki
for .NET Language Integrated Query

Companion book for this site
LINQ to Objects Using C# 4.0:
Using and Extending LINQ to Objects and Parallel LINQ (PLINQ)
Quick Search

Advanced Search »
It is nice to have some more joining possibilities in LINQ. This page tries to explain how they work. Credits to Solid Code for some investigation.

Edit

Inner Join

The default join in LINQ is inner join. It may also be achieved using the Join operator (see Join Operators).

Edit

Left Outer Join

A left outer join can be achieved by using the GroupJoin operator (see Join Operators).

The left outer join can be described with a join clause as follows (see How to: Perform Left Outer Joins):
from outerItem in outer
join innerItem in inner on outerKey equals innerKey into innerGroup
from innerGroupItem in innerGroup.DefaultIfEmpty()
select resultSelector(outerItem, innerGroupItem)

This can be written to an extension function with:
public static IEnumerable<TResult> LeftOuterJoin<TOuter, TInner, TKey, TResult>(
	this IEnumerable<TOuter> outer, IEnumerable<TInner> inner,
	Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector,
	Func<TOuter, TInner, TResult> resultSelector)
{
	return outer.GroupJoin(inner, outerKeySelector, innerKeySelector, 
		(outerItem, innerGroup) => innerGroup.DefaultIfEmpty()
			.Select(innerGroupItem => resultSelector(outerItem, innerGroupItem))
	).SelectMany(result => result);
}

Edit

Right Outer Join

Right outer join is like left outer join, but with tables switched:
public static IEnumerable<TResult> RightOuterJoin<TOuter, TInner, TKey, TResult>(
	this IEnumerable<TOuter> outer, IEnumerable<TInner> inner,
	Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector,
	Func<TOuter, TInner, TResult> resultSelector)
{
	return inner.LeftOuterJoin(outer,
		innerKeySelector, outerKeySelector,
		(innerItem, outerItem) => resultSelector(outerItem, innerItem)
	);
}

Edit

Full Outer Join

Next step is to create a full outer join as a union of left outer join and right outer join.

Using the extension functions we just created:
public static IEnumerable<TResult> FullOuterJoin<TOuter, TInner, TKey, TResult>(
	this IEnumerable<TOuter> outer, IEnumerable<TInner> inner,
	Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector,
	Func<TOuter, TInner, TResult> resultSelector)
{
	return outer.LeftOuterJoin(inner, outerKeySelector, innerKeySelector, resultSelector).Union(
		outer.RightOuterJoin(inner, outerKeySelector, innerKeySelector, resultSelector)
	);
}

Notice one thing: the union here depends on the equality comparison between the results. If the equality between results is not implemented, the result will therefore contain unexpected duplicates. A safer implementation is the following:

public static IEnumerable<TResult> FullOuterJoin<TOuter, TInner, TKey, TResult>(
	this IEnumerable<TOuter> outer, IEnumerable<TInner> inner,
	Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector,
	Func<TOuter, TInner, TResult> resultSelector)
{
	return outer.LeftOuterJoin(inner, outerKeySelector, innerKeySelector, resultSelector).Concat(
		inner.Where(innerItem => 
			!outer.Select(outerItem => outerKeySelector(outerItem)).Contains(innerKeySelector(innerItem))
		).Select(innerItem => resultSelector(default(TOuter), innerItem))
	);
}

If you would like to comment on this page, click on the Discuss button located on the top-right of each page. Feel free to edit any mistakes or omissions you find. If you have an objection or find in-appropriate content then contact the administrator. This website is not affiliated with Microsoft®, all content and opinions are those of the specific author and some advice, solutions and article may contain unintentional errors - please use care. Other websites by this author: Focused Objective, Geek Speak Decoded.