Skip to content

Instantly share code, notes, and snippets.

@andersstorhaug
Last active April 24, 2020 23:13
Show Gist options
  • Select an option

  • Save andersstorhaug/76122879f6ddc353cf38fcc7fc1e2f95 to your computer and use it in GitHub Desktop.

Select an option

Save andersstorhaug/76122879f6ddc353cf38fcc7fc1e2f95 to your computer and use it in GitHub Desktop.
DynamicData `ManyInnerJoin`
internal class ManyInnerJoin<TLeft, TLeftKey, TRight, TRightKey, TDestination>
{
private readonly IObservable<IChangeSet<TLeft, TLeftKey>> _left;
private readonly IObservable<IChangeSet<TRight, TRightKey>> _right;
private readonly Func<TLeft, TRightKey> _rightKeySelector;
private readonly Func<TLeft, TRight, TDestination> _resultSelector;
public ManyInnerJoin(IObservable<IChangeSet<TLeft, TLeftKey>> left,
IObservable<IChangeSet<TRight, TRightKey>> right,
Func<TLeft, TRightKey> rightKeySelector,
Func<TLeft, TRight, TDestination> resultSelector)
{
_left = left ?? throw new ArgumentNullException(nameof(left));
_right = right ?? throw new ArgumentNullException(nameof(right));
_rightKeySelector = rightKeySelector ?? throw new ArgumentNullException(nameof(rightKeySelector));
_resultSelector = resultSelector ?? throw new ArgumentNullException(nameof(resultSelector));
}
public IObservable<IChangeSet<TDestination, TLeftKey>> Run()
{
var leftGroups = _left
.Transform((left, leftKey) => new LeftContainer(left, leftKey))
.Group(leftContainer => _rightKeySelector(leftContainer.Item));
return _right
.InnerJoin(leftGroups, leftGroup => leftGroup.Key, (right, leftGroup) => new ManyJoinContainer(right, leftGroup))
.OnItemRemoved(joinContainer => joinContainer.Dispose())
.MergeMany(joinContainer => joinContainer.LeftGroup.Cache
.Connect()
.Concat(Observable.Defer(() => Observable.Return(GetRemovedChangeSet(joinContainer))))
.Transform(leftContainer => _resultSelector(leftContainer.Item, joinContainer.Right)));
}
private static IChangeSet<LeftContainer, TLeftKey> GetRemovedChangeSet(ManyJoinContainer container)
{
var changes = container.LeftGroup.Cache.Items
.Select(current => new Change<LeftContainer, TLeftKey>(ChangeReason.Remove, current.Key, current))
.ToList();
return new ChangeSet<LeftContainer, TLeftKey>(changes);
}
private class LeftContainer
{
public LeftContainer(TLeft item, TLeftKey key) => (Item, Key) = (item, key);
public TLeft Item { get; }
public TLeftKey Key { get; }
}
private class ManyJoinContainer : IDisposable
{
public ManyJoinContainer(TRight right, IGroup<LeftContainer, TLeftKey, TRightKey> leftGroup) => (Right, LeftGroup) = (right, leftGroup);
public TRight Right { get; }
public IGroup<LeftContainer, TLeftKey, TRightKey> LeftGroup { get; }
public void Dispose() => LeftGroup?.Cache.Dispose();
}
}
public static class ObservableCacheEx
{
public static IObservable<IChangeSet<TDestination, TLeftKey>> ManyInnerJoin<TLeft, TLeftKey, TRight, TRightKey, TDestination>(
this IObservable<IChangeSet<TLeft, TLeftKey>> left,
IObservable<IChangeSet<TRight, TRightKey>> right,
Func<TLeft, TRightKey> leftKeySelector,
Func<TLeft, TRight, TDestination> resultSelector)
{
return new ManyInnerJoin<TLeft, TLeftKey, TRight, TRightKey, TDestination>(left, right, leftKeySelector, resultSelector).Run();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment