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?

+3


source to share


2 answers


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 });

      

+1


source


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.

+1


source







All Articles