Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>The general idea is simple: you merge P and Q, use BufferWithCount(2) to get pairs of values and then process pairs according to your logic:</p> <pre> <code> P.Merge(Q).BufferWithCount(2).Select(values =&gt; { var first = values[0]; var second = values[1]; if (first is P && second is P || first is Q && second is Q) { return 0; } if (first is P) { return selector(first, second); } else // suppose Q, P is a valid sequence as well. { return selector(second, first); } }); </code> </pre> <p>Now the hard part is to merge P and Q if they are of different type and then distinguish between them in Select. If they are of the same type you could use something simple like approach proposed by Enigmativity, i.e.</p> <pre> <code> var pqs = (from p in ps select new { k = "p", v = p }) .Merge(from q in qs select new { k = "q", v = q }); </code> </pre> <p>Now the hard part is if they are of different types, to merge them we'll need some common wrapper type, something like, e.g. <a href="http://www.haskell.org/ghc/docs/latest/html/libraries/base/Data-Either.html" rel="nofollow">Data.Either</a> from Haskell:</p> <pre> <code> public abstract class Either&lt;TLeft, TRight&gt; { private Either() { } public static Either&lt;TLeft, TRight&gt; Create(TLeft value) { return new Left(value); } public static Either&lt;TLeft, TRight&gt; Create(TRight value) { return new Right(value); } public abstract TResult Match&lt;TResult&gt;( Func&lt;TLeft, TResult&gt; onLeft, Func&lt;TRight, TResult&gt; onRight); public sealed class Left : Either&lt;TLeft, TRight&gt; { public Left(TLeft value) { this.Value = value; } public TLeft Value { get; private set; } public override TResult Match&lt;TResult&gt;( Func&lt;TLeft, TResult&gt; onLeft, Func&lt;TRight, TResult&gt; onRight) { return onLeft(this.Value); } } public sealed class Right : Either&lt;TLeft, TRight&gt; { public Right(TRight value) { this.Value = value; } public TRight Value { get; private set; } public override TResult Match&lt;TResult&gt;( Func&lt;TLeft, TResult&gt; onLeft, Func&lt;TRight, TResult&gt; onRight) { return onRight(this.Value); } } } </code> </pre> <p>Funny enough, there is already similar Either class in System.Reactive.dll, unfortunately it's internal, so we need our own implementation. Now we can put both P and Q into Either and proceed with out solution (I've generalized it a bit, so you can return any result instead of int only):</p> <pre> <code> public static IObservable&lt;TResult&gt; SmartZip&lt;TLeft, TRight, TResult&gt;( IObservable&lt;TLeft&gt; leftSource, IObservable&lt;TRight&gt; rightSource, Func&lt;TLeft, TRight, TResult&gt; selector) { return Observable .Merge( leftSource.Select(Either&lt;TLeft, TRight&gt;.Create), rightSource.Select(Either&lt;TLeft, TRight&gt;.Create)) .BufferWithCount(2) .Select(values =&gt; { // this case was not covered in your question, // but I've added it for the sake of completeness. if (values.Count &lt; 2) { return default(TResult); } var first = values[0]; var second = values[1]; // pattern-matching in C# is really ugly. return first.Match( left =&gt; second.Match( _ =&gt; default(TResult), right =&gt; selector(left, right)), right =&gt; second.Match( left =&gt; selector(left, right), _ =&gt; default(TResult))); }); } </code> </pre> <p>And here is a small demo for all this scary ugly stuff.</p> <pre> <code> private static void Main(string[] args) { var psource = Observable .Generate(1, i =&gt; i &lt; 100, i =&gt; i, i =&gt; i + 1) .Zip(Observable.Interval(TimeSpan.FromMilliseconds(10.0)), (i, _) =&gt; i); var qsource = Observable .Generate(1, i =&gt; i &lt; 100, i =&gt; (double)i * i, i =&gt; i + 1) .Zip(Observable.Interval(TimeSpan.FromMilliseconds(30.0)), (i, _) =&gt; i); var result = SmartZip( psource, qsource, (p, q) =&gt; q / p).ToEnumerable(); foreach (var item in result) { Console.WriteLine(item); } } </code> </pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload