using System;
using System.Collections.Generic;
public static class DictionaryJoinExtensions
{
/// <summary>
/// Left-join two dictionaries on their key. Keeps all keys from <paramref name="left"/>.
/// For keys missing on the right, <typeparamref name="TRight"/> is passed as default (null for ref types).
/// </summary>
public static Dictionary<TKey, TResult> LeftJoinByKey<TKey, TLeft, TRight, TResult>(
this IReadOnlyDictionary<TKey, TLeft> left,
IReadOnlyDictionary<TKey, TRight> right,
Func<TKey, TLeft, TRight?, TResult> resultSelector,
IEqualityComparer<TKey>? comparer = null)
where TKey : notnull
{
if (left is null) throw new ArgumentNullException(nameof(left));
if (right is null) throw new ArgumentNullException(nameof(right));
if (resultSelector is null) throw new ArgumentNullException(nameof(resultSelector));
var output = new Dictionary<TKey, TResult>(left.Count, comparer ?? EqualityComparer<TKey>.Default);
foreach (var (key, leftValue) in left)
{
right.TryGetValue(key, out var rightValue);
output[key] = resultSelector(key, leftValue, rightValue);
}
return output;
}
/// <summary>
/// Full outer join of two dictionaries on their key. Includes keys from both sides.
/// For missing values, <typeparamref name="TLeft"/> / <typeparamref name="TRight"/> are passed as default
/// (null for reference types).
/// </summary>
public static Dictionary<TKey, TResult> FullOuterJoinByKey<TKey, TLeft, TRight, TResult>(
this IReadOnlyDictionary<TKey, TLeft> left,
IReadOnlyDictionary<TKey, TRight> right,
Func<TKey, TLeft?, TRight?, TResult> resultSelector,
IEqualityComparer<TKey>? comparer = null)
where TKey : notnull
{
if (left is null) throw new ArgumentNullException(nameof(left));
if (right is null) throw new ArgumentNullException(nameof(right));
if (resultSelector is null) throw new ArgumentNullException(nameof(resultSelector));
var keyComparer = comparer ?? EqualityComparer<TKey>.Default;
// Start with all keys from left, then add keys from right.
var allKeys = new HashSet<TKey>(left.Keys, keyComparer);
allKeys.UnionWith(right.Keys);
var output = new Dictionary<TKey, TResult>(allKeys.Count, keyComparer);
foreach (var key in allKeys)
{
left.TryGetValue(key, out var leftValue);
right.TryGetValue(key, out var rightValue);
output[key] = resultSelector(key, leftValue, rightValue);
}
return output;
}
}