[Feature]: Keep dedicated .Sort(), instead of deprecating
Describe the functionality desired 🐞
With the .Sort() method being depreciated there are a couple of rare cases where applying a sort immediately is desired because the properties used in the comparison do not exist downstream.
example:
changeSet
.Sort(complexSortComparer) //Sorts on input data type
.DistinctValues(x => x.Name) //DistinctValues is transforming to Name data type
.Bind(out names); //List of names is sorted
Changing the call chain to use SortAndBind() is not possible in this scenario.
I had a conversation on Slack about this topic: https://reactivex.slack.com/archives/C4LF8S19N/p1733511277945719
The steps the functionality will provide
- Go to '...'
- Click on '....'
- Scroll down to '....'
- See error
Considerations
SortAndBind
Additional scenarios we discussed...
using var subscription = changes
.Sort()
.Transform() // Does not preserve properties needed for sorting
.Bind()
.Subscribe();
var sharedChanges = changes
.Sort()
.Publish();
using var subscription1 = sharedChanges
.Bind()
.Subscribe();
using var subscription2 = sharedChanges
.Bind()
.Subscribe();
using var connection = sharedChanges
.Connect();
The second scenario is pretty compelling to me, as a potential performance improvement for not duplicating sort work. However, I also proposed in the thread that the performance of .Sort() is so poor that one instance of .Sort() might actually NOT be more efficient thant two instances of .SortAndBind(). Now, I'm curious to benchmark it.
I think I came across another scenario where there's a use case for sorting before binding. If the output collection is a base type, but the observable stream coming in is a derived type.
If there's an elegant work around I'm not seeing, please let me know :)
protected void example(ISourceCache<DerivedType, int> cache)
{
cache
.Connect()
.Sort(derivedTypeSort)
.Transform(x => (BseType)x)
.ObserveOn(uiScheduler)
.Bind(out collection)
.Subscribe()
.DisposeWith(this);
}
private ReadOnlyObservableCollection<BaseType> collection;
public IObservable<SortExpressionComparer<DerivedType>> DerivedTypeSort => //Sort impl
I am a bit late to this show. I meant to respond and promptly forgot.
There is a potentially a solution, and that is to take advantage of the list operators which respect sorted data sets (mostly). Try changing the cache to a list by removing the keys before Sort like this:
changeSet
.RemoveKey() // This converts the operator into a list operator which is index based.
.Sort(complexSortComparer) //Sorts on input data type
.DistinctValues(x => x.Name) //DistinctValues is transforming to Name data type
.Bind(out names); //List of names is sorted
@RolandPheasant how performant is that solution? Seems to me like this SHOULD be decent as long as a user is smart about what comes AFTER the sort?
The performance should be similar to the original cache sort.
I will switch our organization's apps to Roland's suggestion over time. I tested with one project and it seems to work just fine.
I had thought there were some issues with SourceList that didn't occur when using a SourceCache, but maybe those problems were fixed in PR #1009. My requirements for .Sort() before .Bind() are so simple I think it doesn't matter and this will work.
@RolandPheasant the second scenario I posted where the cache is a derived type but the output collection is a base type, is there a work around for that? Or is there a covariance issue with the Bind() or SortAndBind() methods? (See my comment on Feb 3)
there were some issues with SourceList that didn't occur when using a SourceCache
SourceList is fine, it's just limited by a difficulty to ensure good performance. The existing operators are sound and should work well. The only "issue" is that I drew a line in the sand and limited source list to certain operators only. In practise however, I have only ever had a few scenarios where I could not use a cache key or manufacture a quasi one - hence the limited scope.
The advantage however with using the list is that it is naturally suited to binding as a bound list is indexed based and for the most part respected the order.
@jbooth88
cache
.Connect()
.RemoveKey()
.Sort(derivedTypeSort)
.Transform(x => (BseType)x)
.ObserveOn(uiScheduler)
.Bind(out collection)
.Subscribe()
.DisposeWith(this);
Should working fine. Try it out an let me know.
Of the basic list operators, it is only Filter() which does not preserve the order. It's probably possible to fix this, but would require a fair bit of effort.