LINQ - combining and calculating multiple parameters

I have the following

List<Stock> _investments= new List<Stock>()
        {
            new Stock {id=msft, price =12, qty=20}
            new Stock {id=msft, price =53, qty=50}
            new Stock {id=csco, price =222, qty=20}
            new Stock {id=fb, price 40, qty=100}
        }

Given the symbol, say msft, I want to get the total investment and price. In the above case it will be

id=msft, qty=70, price = (12*20 + 53 *50)/70 = 41.29

How to do it in LINQ? I can get 12 * 20 + 53 * 50 this way:

var c = _investments.Where(i => i.ID == investmentName.ToUpper()).Sum(k => k.InitialPrice * k.Qty);

but how to use qty total effectively

+4
source share
3 answers

Use GroupByand Sum:

var result = _investments
   .Where(g => g.Key == "msft")
   .GroupBy(i => i.id)
   .Select(g => new{ id = g.Key,  qty = g.Sum(i => i.qty), grp = g })
   .Select(x => new
   {
       x.id,
       x.qty,
       price = Math.Round(x.grp.Sum(i => (decimal)i.price * i.qty) / x.qty, 2)
   });

Note that I chose the price decimalto prevent integer division (truncates decimal numbers). Math.Roundused to round the result in two decimal places.

+2
source

The simplest way:

var relevantInvestments = _investments
    .Where(i => i.ID == investmentName.ToUpper())
    .ToArray();

var totalQty = relevantInvestments.Sum(k => k.Qty);
var c = relevantInvestments.Sum(k => k.InitialPrice * k.Qty) / totalQty;

Repeat only once:

var c = _investments
    .Where(i => i.ID == investmentName.ToUpper())
    .Aggregate(
        new { Price = 0, TotalQty = 0 },
        (total, i) => new 
                      { 
                          Price = total.Price + i.InitialPrice * i.Qty,
                          TotalQty = total.TotalQty + i.Qty
                      },
        i => i.Price / i.TotalQty);
+2

A good way to include computed values ​​might be to create a class for this. Thus, you clearly wrote and typed the values ​​without using anonymous types, and you will make sure that the work on its calculation occurs only once. For simplicity, I use doubleeverywhere here, but stringfor identifiers that may or may not match your types ( decimalprobably should be used for monetary amounts).

class StockGroup
{
    public List<Stock> Stocks { get; private set; }

    public string Id { get; private set; }
    public double TotalPrice { get; private set; }
    public double AveragePrice { get; private set; }
    public double Qty { get; private set; }

    public StockGroup(string id, IEnumerable<Stock> stocks)
    {
        this.Id = id;
        this.Stocks = stocks.ToList();
        foreach (var stock in this.Stocks)
        {
            this.Qty += stock.Qty;
            this.TotalPrice += stock.Price * stock.Qty;
        }
        this.AveragePrice = this.TotalPrice / this.Qty;
    }
}

// use like
var groups =
         _investments.GroupBy(x => x.Id).Select(x => new StockGroup(x.Key, x));
+2
source

All Articles