Linq operate with aggregate function - linq

I need to get a calculation on aggregation from linq which I hope someone can help
I have a list of objects that have 3 fields (date, saleprice and productcode) I need to get FOR EACH date (Group by date), the SUM of saleprice
/ COUNT of distinct product code.
I know how I can find the SUM alone but not calculation by another aggregate

It would be easier to answer your question with some sample code and objects. I'll assume, items is your list of objects:
items.GroupBy(obj => obj.Date)
.Select(g => new
{
Date = g.Key.Date,
Aggregate = g.Sum(obj => obj.SalePrice) / g.Select(obj => obj.ProductCode)
.Distinct().Count()
});

Related

Linq to get row with a minimum value of a column but based on another column value

I have seen related questions and answer but none of those helped me.
I have created a linq query to get an IEnumerable list like this -
**price** **car_name** **location**
23 carA locationA
43 carA locationB
56 carA locationC
12 carB locationA
34 carB locationB
46 carB locationC
Now I want only rows with minimum price of each car...
Thanks in advance.
EDIT : Sorry I wasn't clear about my question but I want all the location too...I show that in a separate dropdown list.
You need to Group the Collection based on Car Name and then Order each group by price so that you can the minimum.
var result = carList.GroupBy(x=>x.Name)
.Select(x=>x.OrderBy(p=>p.Price).First());
If you want to avoid the Order By, you could make use of Aggregate.
var result = list.GroupBy(x => x.Name)
.Select(g => g.Aggregate((c1, c2) => c1.Price < c2.Price ? c1 : c2));
Aggregate would iterate only one time over the collection, keeping track of the car with minimum price.
In either case, output sample,
from cs in myContext.CarStore
group cs by cs.car_name into cgroup
select new {
Car_Name= cgroup.Key,
Price= cgroup.Min(cs=> cs.price)});
i think this may help..
Others came up with the idea to group your cars into groups with same CarName. Then they order all CarPrices in each group, after which they take the FirstOrDefault CarPrice.
It is a bit of a waste to Order all CarPrices if you only want the minimum CarPrice
For this I use the GroupBy overload with KeySelector, ElementSelector and ResultSelector
var cheapestCars = allCars.GroupBy(
// key selector:
car => car.CarName,
// element selector
car => car.CarPrice,
// result selector:
(carName, pricesOfCarsWithThisCarName) => new
{
CarName = carName,
LowestAvailablePrices = pricesOfCarsWithThisCarName.Min(),
});

How to get top 10 customers based on values of orders

I have a list of Customers who each have a list of Orders. Each Order has a list of LineItems.
I would like to write a LINQ query that would get me the top 10 customers based on order value (i.e. money spent) and not the total number of orders.
One customer could have 2 orders but could have spent £10,000, but another customer could have 100 orders, and only spent £500.
Right now, I have this which gets me the top 10 customers by the number of orders.
var customers = (from c in _context.Customers where c.SaleOrders.Count > 0
let activeCount = c.SaleOrders.Count(so => so.Status != SaleOrderStatus.Cancelled)
orderby activeCount descending
select c).Take(10);
UPDATE
Thanks to Jon Skeet's comment about doing a double Sum, I wrote the following query which compiles.
var customers = (from c in _context.Customers where c.SaleOrders.Count > 0
let orderSum = c.SaleOrders.Where(so => so.Status != SaleOrderStatus.Cancelled)
.Sum(so => so.LineItems.Sum(li => li.CalculateTotal()))
orderby orderSum descending
select c).Take(10);
But when I run this, I get the following error:
It seems LINQ doesn't recognise my .CalculateTotal() method which sit on my LineItem.cs entity.
The problem you were seeing is that CalculateTotal() is not something that Linq can translate into SQL (which is done at run-time, hence no complier error).
The essential problem here is that Linq doesn't really work on lambdas (Func<>), but actually Expressions (Expression<Func<>>), which is the code in a partial compiled state, which Linq then goes about disassembling and translating into SQL.
So, let assume CalculateTotal is a member function defined like this:
public decimal CalculateTotal()
{
return this.quantity * this.value;
}
We could define that as a local lambda function
Func<LineItem, decimal> CalculateTotal = (li => li.quantity * li.value);
Now, we have a lambda which takes a LineItem and returns a value, which is exactly what Sum() wants, so now we can replace:
.Sum(so => so.LineItems.Sum(li => li.CalculateTotal()))
with
.Sum(so => so.LineItems.Sum(CalculateTotal))
But that will crash, just as it did before, because, as I said, it wants an Expression. So, we give it one:
Expression<Func<LineItem, decimal>> CalculateTotal = (li => li.quantity * li.value);

LINQ query NotSupportedException OrderBy Average of TimeSpan

var seasonPlayer = (from SeasonPlayer in db.SeasonPlayerSet
orderby SeasonPlayer.StatisticsPlayer.Average(x => x.STP_timeplay.Ticks) descending
select SeasonPlayer).ToList();
SeasonPlayer has an ICollection of StatisticsPlayer so i want to get a average of time spent on the court ordered descending by STP_timeplay which is a typ of TimeSpan. I can't get average by STP_timeplay because it isn't a decimal so i tried get average by Ticks. It throws an exception:
The specified type member 'Ticks' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
The problem is that the Linq to Entities query provider isn't able to translate your LINQ into a Sql query which joins to the Statistics Player, averages the timeplay, grouped by season player.
Given that you appear to be iterating all Season Players, if the number of records isn't too large you could bring this all into memory like so:
var seasonPlayer = db.SeasonPlayerSet
.Include(sp => sp.StatisticsPlayer)
.ToList()
.Select(sp => new {SeasonPlayer = sp, Average = sp.StatisticsPlayer.Average(stp => stp.STP_timeplay.Ticks)})
.OrderByDescending(sp => sp.Average)
.Select(sp => SeasonPlayer)
.ToList();
Try this:-
var seasonPlayer = db.SeasonPlayerSet.ToList()
.OrderByDescending(x => x.StatisticsPlayer
.Average(z => z.STP_timeplay.Ticks);

linq query with count

I would like to create a simple linq query, but I don't really know, how it should be. I have searched the net, but found nothing, what I can use or I don't know yet, that I could use it.
So, basically, I have a table with this fields: reference, vat_code, amount, vat_amount, supplier.
Now, I would like to query the records, where reference<>'' and the reference is more than once in the table. But I need all the occupians.
F.e. from
1;VF;100;27;345
2;VF;200;54;123
2;VF;-200;-54;123
2;VF;200;54;123
3;VF;300;81;888
to
2;VF;200;54;123
2;VF;-200,-54;123
2;VF;200;54;123
How would be look the linq query to this?
Thanks.
If rows with same reference should go together in results, then you should filter out rows with reference equal to empty string, then group all rows by reference and select only those groups which have more than one row.
C# sample with DataTable:
var result = dt.AsEnumerable()
.Where(r => r.Field<string>("reference") != "")
.GroupBy(r => r.Field<string>("reference"))
.Where(g => g.Count() > 1)
.SelectMany(g => g); // flatten group to sequence of rows

Linq query with group by clause

My table has following columns:-
LevelId, LevelName, ScenarioId, TeamId.
I want to select LevelName corresponding to minimum LevelId when grouped by teamId
So, you want to:
Group by team ID
Within each group, order by level ID
Take the first result's level name
So that sounds like:
var query = table.GroupBy(x => x.TeamId)
.Select(g => g.OrderBy(x => x.LevelId).First().Name);
Don't just take this query and include it in your code though - make sure you understand it so you can come up with your own solution next time you have something similar to do.
If this is in LINQ to Objects, you may find that MoreLINQ would be useful, with its MinBy method:
var query = table.GroupBy(x => x.TeamId)
.Select(g => g.MinBy(x => x.LevelId).Name);
This avoids ordering the whole group, just to find the entry with the minimum level ID.

Resources