Net Dots

.Net and programming in general

Making list navigation responsive using Reactive Extensions-part 3 (CombineVeryLatest)

Let’s take a look at creating a variation of CombineLatest operator which we used as a part of SampleResponsive extension in the previous article in the series. We needed an operator that would combine two most recent elements from source observables on one-to-one basis (in the combined observable, no element from any of the sources would appear twice). CombineLatest almost makes the first part of this requirement, and we just need to filter out extra pairs that contain duplicate elements.

The first trick we will use is a Select overload which allows us to generate id’s attached to our elements. The resulting observable contains pairs of tuples, each containing both the real value and id:

                 return  Observable .CombineLatest(
                     leftSource.Select(Tuple .Create<TLeft, int >),
                     rightSource.Select(Tuple .Create<TRight, int >),
                         (x, y) => new  { x, y })
  

Now, we can introduce a “state” to our extension which would track the latest id’s and also make possible to filter the oncoming elements comparing to that id’s. We update the state by inserting a side-effecting extension .Do right after the filter:

         public  static  IObservable <TResult> CombineVeryLatest<TLeft, TRight, TResult>(this  IObservable <TLeft> leftSource,
           IObservable <TRight> rightSource, Func <TLeft, TRight, TResult> selector)
         {
             int  l = -1, r = -1;
             return  Observable .CombineLatest(
                 leftSource.Select(Tuple .Create<TLeft, int >),
                 rightSource.Select(Tuple .Create<TRight, int >),
                     (x, y) => new  { x, y })
                 .Where(t => t.x.Item2 != l && t.y.Item2 != r)
                 .Do(t => { l = t.x.Item2; r = t.y.Item2; })
                 .Select(t => selector(t.x.Item1, t.y.Item1));
         }
  

This is the same signature as CombinedLatest has. The only thing left is to isolate the state (l and r variables) within the extension from multiple subscribers. This is usually done by using the .Defer() method (which would also make our observable Cold):

         public  static  IObservable <TResult> CombineVeryLatest<TLeft, TRight, TResult>(this  IObservable <TLeft> leftSource,
           IObservable <TRight> rightSource, Func <TLeft, TRight, TResult> selector)
         {
             return  Observable .Defer(() =>
             {
                 int  l = -1, r = -1;
                 return  Observable .CombineLatest(
                     leftSource.Select(Tuple .Create<TLeft, int >),
                     rightSource.Select(Tuple .Create<TRight, int >),
                         (x, y) => new  { x, y })
                     .Where(t => t.x.Item2 != l && t.y.Item2 != r)
                     .Do(t => { l = t.x.Item2; r = t.y.Item2; })
                     .Select(t => selector(t.x.Item1, t.y.Item1));
             });
         }
 

All done! This extension is useful in situations when you need to combine pairs of values that “expire” after being combined.

February 9, 2011 Posted by | Uncategorized | Leave a comment