Conditional pairing of two streams - Combine If in Reactive Extensions

203 Views Asked by At

I am looking to combine the first two sequences as this Marble diagram depicts

CoincidenceWhere

  1. In this example different lottery players pick numbers, and the numbers they pick are indicated by the color of the small dot on the first line.
  2. When winning numbers are decided (line 2)
  3. Lottery winners need to be determined (output on line 3). For lottery winners the small dot represents the player that won.

Desired Outputs

  • So for the red winning numbers, it is not the latest lottery player that won, but it happens to be the third black player
  • For the brown winning numbers it is the first blue player that should be put on the output stream
  • And for the last red winning numbers, it should the last red numbers that won (white player) and not the first red numbers (black player)
  • Note that the output should contain both the player and the numbers (objects), including their associated properties such as player name, and amount of money won associated with the winning numbers etc (this could be achieved by having a selector Func)

Rx Solutions?

  • I tried using .Zip, .When, .CombineLatest, .Buffer, etc, but could not achieve the results desired?
  • Is there anything native in the API that can be used or what kind of custom combination would achieve the desired outputs?
1

There are 1 best solutions below

2
On BEST ANSWER

Your choice of colours for the players and colours for the numbers made things a bit confusing.

Nevertheless, this worked for me.

Start with:

var picks = new Subject<KeyValuePair<string, string>>(); // key == number, value = player
var numbers = new Subject<string>();

Then build a sequence of dictionary of picks by number:

var scanned =
    picks
        .Scan(new Dictionary<string, string>(), (d, kvp) =>
        {
            // You need a new dictionary each time
            var d2 = new Dictionary<string, string>(d);
            d2[kvp.Key] = kvp.Value;
            return d2;
        });

Now combine using .Switch():

var query =
    scanned
        .Select(d =>
            numbers.Select(n => new
            {
                player = d[n],
                number = n
            }))
        .Switch();

That's it.

Here's the code that runs it:

query.Subscribe(x => Console.WriteLine(x));

picks.OnNext(new KeyValuePair<string, string>("Brown", "Blue"));
picks.OnNext(new KeyValuePair<string, string>("Grey", "Green"));
picks.OnNext(new KeyValuePair<string, string>("Red", "Black"));
picks.OnNext(new KeyValuePair<string, string>("Grey", "Yellow"));
numbers.OnNext("Red");
numbers.OnNext("Brown");
picks.OnNext(new KeyValuePair<string, string>("Grey", "Black"));
picks.OnNext(new KeyValuePair<string, string>("Red", "White"));
numbers.OnNext("Red");

Here are the results I got:

results