Sometimes when you want to manage your code efficiency you may need to optimize some .Net implementation.
Jointure implementation in C# may be such a case.
Underneath you will find my proposal to implement a jointure in C# environment. This is a generic code and may be optimized in many way for your implementation needs. It use System.Linq but remediation can be done very easily to not depend on this library.
using System;
using System.Collections.Generic;
using System.Linq;
namespace org.maximilienzakowski.Jointures
{
public static class Extensions
{
public static TJ[] InnerJoin<TL, TR, TK, TJ>(this IEnumerable<TL> lefts, IEnumerable<TR> rights, Func<TL, TK> onLeftKey, Func<TR, TK> onRightKey,
Func<TL, TR, TJ> jointureBuilder)
{
var leftsWithKeys = lefts.Select(l => new Tuple<TL, TK>(l, onLeftKey(l)));
var rightsWithKeys = rights.Select(r => new Tuple<TR, TK>(r, onRightKey(r)));
var rightGroupsByKey = rightsWithKeys.GroupBy(r => r.Item2, m => m.Item1).ToDictionary(m => m.Key, m => m.ToArray());
var defaultJoineds = new TR[] { };
var enumerable = leftsWithKeys.SelectMany(m =>
{
TR[] joineds = defaultJoineds;
if (m.Item2 != null)
{
rightGroupsByKey.TryGetValue(m.Item2, out joineds);
}
return (joineds ?? defaultJoineds).Select(joined => jointureBuilder(m.Item1, joined));
});
return enumerable.ToArray();
}
public static Tuple<TL,TR>[] LeftJoin<TL,TR,TK>(this IEnumerable<TL> lefts, IEnumerable<TR> rights, Func<TL,TK> onLeftKey, Func<TR,TK> onRightKey)
{
return LeftJoin(lefts, rights, onLeftKey, onRightKey, (left, right) => new Tuple<TL, TR>(left, right), default(TR));
}
public static Tuple<TL,TR>[] LeftJoin<TL,TR,TK>(this IEnumerable<TL> lefts, IEnumerable<TR> rights,
Func<TL,TK> onLeftKey, Func<TR,TK> onRightKey,TR defaultJoined)
{
return LeftJoin(lefts, rights, onLeftKey, onRightKey, (left, right) => new Tuple<TL, TR>(left, right), defaultJoined);
}
public static IList<TJ> LeftJoin<TL,TR,TK,TJ>(this IEnumerable<TL> lefts,IEnumerable<TR> rights, Func<TL,TK> onLeftKey,Func<TR,TK> onRightKey, Func<TL,TR,TJ> jointureBuilder)
{
return LeftJoin(lefts, rights, onLeftKey, onRightKey, jointureBuilder, default(TR));
}
public static TJ[] LeftJoin<TL,TR,TK,TJ>(this IEnumerable<TL> lefts, IEnumerable<TR> rights, Func<TL,TK> onLeftKey,Func<TR,TK> onRightKey,
Func<TL,TR,TJ> jointureBuilder, TR defaultJoined)
{
var leftsWithKeys = lefts.Select(l => new Tuple<TL, TK>(l, onLeftKey(l)));
var rightsWithKeys = rights.Select(r => new Tuple<TR, TK>(r, onRightKey(r)));
var rightGroupsByKey = rightsWithKeys.GroupBy(r => r.Item2, m => m.Item1).ToDictionary(m => m.Key, m => m.ToArray());
var defaultJoineds = new TR[] { defaultJoined };
var enumerable = leftsWithKeys.SelectMany(m =>
{
TR[] joineds = defaultJoineds;
if(m.Item2 != null)
{
rightGroupsByKey.TryGetValue(m.Item2, out joineds);
}
return (joineds ?? defaultJoineds).Select(joined => jointureBuilder(m.Item1, joined));
});
return enumerable.ToArray();
}
public static Tuple<TL,TR>[] FullJoin<TL,TR,TK>(this IEnumerable<TL> lefts, IList<TR> rights,
Func<TL,TK> leftKeyFunc, Func<TR,TK> rightKeyFunc, TL defaultLeft = default(TL), TR defaultRight = default(TR))
{
var leftJoinedToRight =
lefts.LeftJoin(rights, leftKeyFunc, rightKeyFunc, defaultRight).ToArray();
var joinedRightKeys =
new HashSet<TK>(leftJoinedToRight
.Where(m => m.Item2 != null)
.Select(m => rightKeyFunc(m.Item2)).Distinct());
var defaultLeftAndMissingRights =
rights
.Select(m => Tuple.Create(m, rightKeyFunc(m)))
.Where(m => !joinedRightKeys.Contains(m.Item2))
.Select(m => Tuple.Create(defaultLeft, m.Item1)).ToArray();
var fullJointure = leftJoinedToRight.Concat(defaultLeftAndMissingRights).ToArray();
return fullJointure;
}
}
}