Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a ReactiveUI derived collection with more elements than the original

Is it possible to create a ReactiveUI derived collection that has more elements in it than the original?

I've seen that there is a way of filtering a collection, and selecting single properties, but what I'm looking for is the equivalent of the SelectMany operation for enumerables.

To illustrate, imagine trying to get a derived collection representing every passenger stuck in a traffic jam.

class Car 
{
    ReactiveCollection<Passenger> Passengers;
}

var TrafficJam=new ReactiveCollection<Car>();
EveryPassengerInTheTrafficJam=Cars.CreateDerivedCollection(c=>c.Passengers);

The above doesn't work, I think the error was IEnumerable<ReactiveCollection<Passenger>> can't be cast to ReactiveCollection<Passenger> - or something up with the types, in any case.

I can't figure out the right approach for this flattening - admittedly I may be barking up completely the wrong tree here, so please let me know if there's a better way of achieving the same thing!

like image 873
Daniel Neal Avatar asked Mar 06 '13 18:03

Daniel Neal


2 Answers

At the moment, CreateDerivedCollection doesn't support SelectMany as a transformation, it gets too Tricky to handle removes. If you don't have many items, you can just regenerate the collection every time:

cars.Changed
    .Select(_ => cars.SelectMany(x => x.Passengers).ToList())
    .ToProperty(this, x => x.Passengers);

Edit: Alright, here we go:

var whenCarsOrPassengersInThoseCarsChange = Observable.Merge(
    cars.Changed
        .SelectMany(_ =>
            cars.Select(x => x.Passengers.Changed).Merge())
        .Select(_ => Unit.Default),
    cars.Changed.Select(_ => Unit.Default));

whenCarsOrPassengersInThoseCarsChange.StartWith(Unit.Default)
    .Select(_ => cars.SelectMany(x => x.Passengers).ToList())
    .ToProperty(this, x => x.Passengers);

So, the idea is that we've got two main situations when we want to reevaluate the passengers list:

  1. When one of the passengers change in the cars
  2. When one of the cars change

However, the tricky part is, we only want to watch passengers for cars in the collection (i.e. if a car is removed, we no longer care about its passengers).

Properly tracking suicidal passengers

So, the idea in that weird SelectMany is, "Every time the car list changes, build a new list of Observables that represent when the passenger collection changes, and merge them all together".

However, if we only had that statement, we would have to wait for a car to be added and its passengers change before we got a new passenger list, so we also have to update when the list of cars change too.

What's this "Unit" business?

In this case, I actually don't care about the values that these Observables put out, just when they happen. "Unit" is the Rx version of void, it only has a single value, "Unit.Default". You use it when you only care when something happens, not what the value of it is.

like image 146
Ana Betts Avatar answered Nov 06 '22 10:11

Ana Betts


Hmm...I'm not all that familiar with ReactiveUI, but just reading thru, it looks like you need to alter where your SelectMany goes:

var jam = cars.SelectMany(x => x.Passengers).CreateDerivedCollection(p => p);
like image 33
JerKimball Avatar answered Nov 06 '22 11:11

JerKimball