Skip to content

Instantly share code, notes, and snippets.

@Argon42
Last active March 5, 2025 10:53
Show Gist options
  • Save Argon42/66aa8707c13c2c33246aacbb506eb0da to your computer and use it in GitHub Desktop.
Save Argon42/66aa8707c13c2c33246aacbb506eb0da to your computer and use it in GitHub Desktop.
UniRx aggregate RxProperty from RxCollection
public static class UniRxExtensions
{
public static IObservable<TResult> AggregateRxProperties<TItem, TPropertyValue, TResult>(
this IReadOnlyReactiveCollection<TItem> collection,
Func<TItem, IReadOnlyReactiveProperty<TPropertyValue>> propertySelector,
Func<IEnumerable<TPropertyValue>, TResult> resultSelector)
{
var disposable = new CompositeDisposable();
var dictionary = new Dictionary<TItem, IDisposable>();
var subject = new Subject<TResult>().AddTo(disposable);
subject.Subscribe(_ => { }, () =>
{
ResetWithoutNotification();
disposable.Dispose();
});
collection.ObserveAdd().Subscribe(@event => AddElement(@event.Value)).AddTo(disposable);
collection.ObserveRemove().Subscribe(@event => RemoveElement(@event.Value)).AddTo(disposable);
collection.ObserveReset().Subscribe(_ => Reset()).AddTo(disposable);
collection.ObserveReplace().Subscribe(Replace).AddTo(disposable);
subject.OnNext(GetResult());
return subject;
void AddElement(TItem item)
{
IDisposable dispose = propertySelector(item).Subscribe(_ => subject.OnNext(GetResult()));
dictionary[item] = dispose;
}
TResult GetResult() => resultSelector(collection.Select(item => propertySelector(item).Value));
void RemoveElement(TItem item)
{
dictionary[item].Dispose();
dictionary.Remove(item);
subject.OnNext(GetResult());
}
void Replace(CollectionReplaceEvent<TItem> @event)
{
dictionary[@event.OldValue].Dispose();
dictionary.Remove(@event.OldValue);
IDisposable dispose = propertySelector(@event.NewValue).Subscribe(_ => subject.OnNext(GetResult()));
dictionary[@event.NewValue] = dispose;
}
void Reset()
{
ResetWithoutNotification();
subject.OnNext(GetResult());
}
void ResetWithoutNotification()
{
dictionary.Values.ForEach(disposable1 => disposable1.Dispose());
dictionary.Clear();
}
}
}
@Argon42
Copy link
Author

Argon42 commented Mar 5, 2025

An example of how I use this

IsValid = ResourcePrice
                .AggregateRxProperties(model => model.IsValid,
                    collection => collection.All(value => value))
                .CombineLatest(ResourcePrice.ObserveCountChanged(true),
                    (isValid, count) => isValid || count == 0)
                .ToReadOnlyReactiveProperty()
                .AddTo(CompositeDisposable);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment