using System;
using System.Collections.Generic;
using System.Linq;
static class Program
{
static void Main(string[] args)
{
var ax = new[] {
new { id = 1, name = "John" },
new { id = 2, name = "Sue" } };
var bx = new[] {
new { id = 1, surname = "Doe" },
new { id = 3, surname = "Smith" } };
ax.FullOuterJoin(
bx, a => a.id, b => b.id,
(a, b, id) => new { a.name, b.surname },
new { id = -1, name = "(no firstname)" },
new { id = -2, surname = "(no surname)" }
)
.ToList().ForEach(Console.WriteLine);
}
}
internal static class MyExtensions
{
internal static IList
FullOuterGroupJoin(
this IEnumerable a,
IEnumerable b,
Func selectKeyA,
Func selectKeyB,
Func, IEnumerable, TK, TR> projection,
IEqualityComparer cmp = null)
{
cmp = cmp?? EqualityComparer.Default;
var alookup = a.ToLookup(selectKeyA, cmp);
var blookup = b.ToLookup(selectKeyB, cmp);
var keys = new HashSet(alookup.Select(p => p.Key), cmp);
keys.UnionWith(blookup.Select(p => p.Key));
var join = from key in keys
let xa = alookup[key]
let xb = blookup[key]
select projection(xa, xb, key);
return join.ToList();
}
internal static IList FullOuterJoin(
this IEnumerable a,
IEnumerable b,
Func selectKeyA,
Func selectKeyB,
Func projection,
TA defaultA = default(TA),
TB defaultB = default(TB),
IEqualityComparer cmp = null)
{
cmp = cmp?? EqualityComparer.Default;
var alookup = a.ToLookup(selectKeyA, cmp);
var blookup = b.ToLookup(selectKeyB, cmp);
var keys = new HashSet(alookup.Select(p => p.Key), cmp);
keys.UnionWith(blookup.Select(p => p.Key));
var join = from key in keys
from xa in alookup[key].DefaultIfEmpty(defaultA)
from xb in blookup[key].DefaultIfEmpty(defaultB)
select projection(xa, xb, key);
return join.ToList();
}
}