Describe the functionality desired 馃悶
Consider the following new filtering operators, which should provide performance improvements over existing operators:
public static IObservable<IChangeSet<TObject, TKey>> Filter<TObject, TKey, TState>(
this IObservable<IChangeSet<TObject, TKey>> source,
IObservable<TState> state,
Func<TState, TObject, bool> predicate,
bool suppressEmptyChangeSets = true);
public static IObservable<IChangeSet<TItem>> Filter<TItem, TState>(
this IObservable<IChangeSet<TItem>> source,
IObservable<TState> state,
Func<TState, TItem, bool> predicate,
bool suppressEmptyChangeSets = true);
public static IObservable<IChangeSet<TObject, TKey>> FilterValues<TObject, TKey>(
this IObservable<IChangeSet<TObject, TKey>> source,
Func<TObject, bool> predicate,
bool suppressEmptyChangeSets = true);
Not set on the name for the third one, but it stems from a conversation with @RolandPheasant and @dwcullop in Slack. Alternatives for this would maybe be .FilterSlim() or .FilterDeterministic() or .FilterWithoutCaching(). Whatever the name, the goal would be to keep it consistent with other operators (like .Transform()) that normally keep an internal items cache, but could have versions that don't.
The steps the functionality will provide
For the first two operators, utilizing an IObservable<TState> parameter, the primary improvement operator is to reduce memory allocations required by existing operators that support dynamic filtering.
The third operator would improve performance for static-filtering scenarios, where the filter predicate is relatively fast, and is known to be deterministic, I.E. collection items are immutable, at least from the perspective of filtering. Under this scenario, .Filter() can be implemented without the need for an internal cache of items, becoming allocation-free for all single-change ChangeSets.
This operator would not be applicable as an ObservableList operator, as using deterministic-filtering does not eliminate the need for an internal items cache. An internal cache is still needed to track item indexes, so downstream indexes can be adjusted for items filtered out of the source.
Considerations
- Consumers can currently use
.Filter() with the IObservable<Func<TObject, bool>> or IObservable<Func<TObject, TKey, bool>> parameters to dynamically control filtering, but this requires either a new Func<> allocation for each change in filtering rules, or a decent bit of tedious code to reuse and re-emit a shared Func<> instance each time, while instead mutating its closure.
- Consumers can currently use
.FilterOnObservable(), but this requires multiple object allocations per-added-item, to create an IObservable<bool> that controls filtering of that item, and to subscribe to that stream. This approach also doesn't really support collection items being immutable, at all, as replace operations force a teardown and reconstruction of the filtering stream for that item.
Describe the functionality desired 馃悶
Consider the following new filtering operators, which should provide performance improvements over existing operators:
Not set on the name for the third one, but it stems from a conversation with @RolandPheasant and @dwcullop in Slack. Alternatives for this would maybe be
.FilterSlim()or.FilterDeterministic()or.FilterWithoutCaching(). Whatever the name, the goal would be to keep it consistent with other operators (like.Transform()) that normally keep an internal items cache, but could have versions that don't.The steps the functionality will provide
For the first two operators, utilizing an
IObservable<TState>parameter, the primary improvement operator is to reduce memory allocations required by existing operators that support dynamic filtering.The third operator would improve performance for static-filtering scenarios, where the filter predicate is relatively fast, and is known to be deterministic, I.E. collection items are immutable, at least from the perspective of filtering. Under this scenario,
.Filter()can be implemented without the need for an internal cache of items, becoming allocation-free for all single-change ChangeSets.This operator would not be applicable as an
ObservableListoperator, as using deterministic-filtering does not eliminate the need for an internal items cache. An internal cache is still needed to track item indexes, so downstream indexes can be adjusted for items filtered out of the source.Considerations
.Filter()with theIObservable<Func<TObject, bool>>orIObservable<Func<TObject, TKey, bool>>parameters to dynamically control filtering, but this requires either a newFunc<>allocation for each change in filtering rules, or a decent bit of tedious code to reuse and re-emit a sharedFunc<>instance each time, while instead mutating its closure..FilterOnObservable(), but this requires multiple object allocations per-added-item, to create anIObservable<bool>that controls filtering of that item, and to subscribe to that stream. This approach also doesn't really support collection items being immutable, at all, as replace operations force a teardown and reconstruction of the filtering stream for that item.