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(); } }