Creating anonymous object via LINQ from an IObservable?
I inherited the byzantine API, which accesses remote data via JSON, the requests themselves are processed via the TPL, which, for reasons I won't go into, pushes me into a corner.
So I have an IObservable subscription that I am requesting, for example:
mcSub.Take(1).Subscribe(x => x.Markets.ForEach(i => i.Prices.AvailableToBuy.ForEach(t => tabPanel.textBox1.AppendText(i.Id + " Back \t" + t.Size + " @ " + t.Price))));
Obviously Markets and AvailableToBuy are lists, my question is, how can I create a LINQ query / object to retrieve i.Id, t.Size and t.Price?
source to share
I believe you can do it like this by flattening twice (once markets and then prices) in one collection with all prices:
//get all prices available to buy:
var pricesAvailableToBuy = mcSub.SelectMany(x => x.Markets)
.SelectMany(y => y.Prices.AvailableToBuy)
.Select(p => new { p.Id, p.Size, p.Price });
This gives you all prices for all markets in mcSub
, while your original operator only works on one item (which also depends on what it is called - if it makes sense on a worker consumer's task / thread).
Or similar wording:
var pricesAvailableToBuy = mcSub.SelectMany(x => x.Markets.SelectMany(y => y.Prices.AvailableToBuy))
.Select(p => new { p.Id, p.Size, p.Price });
source to share
Woof.
This is one nasty phrase about subscription / request. Let's see if we can clean up a bit ... I see that you are taking 1 from mcSub and subscribing to it, so I will assume it is enumerable for observables?
Let's first fake some types that I can reference (I'll do my best to infer from your example):
First, the data items:
public class Thing
{
public List<Market> Markets {get; set;}
}
public class Market
{
public Price Prices {get; set;}
}
public class Price
{
public List<AvailablePrice> AvailableToBuy {get; set;}
}
public class AvailablePrice
{
public string Id {get; set;}
public int Size {get; set;}
public decimal Price {get;set;}
}
Next, the thing that will generate IEnumerable<IObservable<Thing>>
that I will ham-handedly hack with:
public IEnumerable<IObservable<Thing>> GetThingsObs()
{
var rnd = new Random();
return Enumerable.Range(0, 3).Select(_ => {
return Observable.Create<Thing>(obs =>
{
var things = Enumerable.Range(0, 3).Select(i => new Thing()
{
Markets = Enumerable.Range(0, 3).Select<int, Market>(x =>
{
return new Market()
{
Prices = new Price
{
AvailableToBuy = Enumerable.Range(0, 3)
.Select(y => new AvailablePrice { Id = string.Format("{0}:{1}:{2}", i, x, y), Size = rnd.Next(0, 10), Price = rnd.Next(0, 20) })
.ToList()
}
};
}).ToList()
});
foreach(var thing in things)
obs.OnNext(thing);
// this bit is important, but I'll get back to it later
obs.OnCompleted();
return Disposable.Empty;
});
});
}
Okay, now we have something that (I hope) matches your data a bit, let's query!
If the observable is known to have completed (the bit above I am obscured), you can safely convert IObservable<T>
to IEnumerable<T>
- if it is not completed it will mostly hang, so be careful!
var mcSub = GetThingsObs();
var query =
// for each observable
from obs in mcSub
// this replaces your subscribe calls, but the observable MUST complete
// for this to return properly!
from thing in obs.ToEnumerable()
// After that, it just multiple SelectManys
// (which is what the nested 'from x in y' calls get translated into)
// For each market
from market in thing.Markets
// for each price thingy
from price in market.Prices.AvailableToBuy
// select out your stuff
select new { price.Id, price.Size, price.Price };
This is the best I can make from the sample you provided - if you can provide more information / details, I'll take another shot if that doesn't work for you.
source to share