LINQ query to find average cost per item by region - linq

I have a winforms app and a database consisting of shop data and item sales data. In the data retrieved from the database I would like to filter the resultant object collection using LINQ. What LINQ query do I need to join the Shop and ItemSale objects and query them to select group by Region and the average cost for each item?
Shop.cs
StoreId
Name
Region
ItemSale.cs
Name
Cost
Region
Required output:
Region Name | Average Item cost
Region1 | 22.50
Region2 | 15.30
Region3 | 15.90

I like to use linq expression queries when working with joins.
var querySelect =
from r in Shop
join i in ItemSale on r.Region equals i.Region
select new
{
Region = r.Region,
ItemCost = r.Cost
};
I find it easier to use lambda linq queries for groups
var queryGroup = querySelect
.GroupBy(m => new { m.Region }
.Select(m => new
{
Region = m.Key.Region,
AverageItemCost = m.Average(x => x.ItemCost)
};
You could do both of these queries in one, but I figured it would be easier to understand as two separate queries and there is no significant performance gain from combining them.

Related

How to write SQL translateable linq code that groups by one property and returns distinct list

I want to change code below to be sql translateable because now i get exception.
Basicallly i want list of customers from certain localisation and there could be more than one customer with the same CustomerNumber so i want to take the one that was most recently added.
In other words - distinct list of customers from localisation where "distinct algorithm" works by taking the most recently added customer if there is conflict.
The code below works only if it is client side. I could move Group By and Select after ToListAsync but i want to avoid taking unnecessary data from database (there is include which includes list that is pretty big for every customer).
var someData = await DbContext.Set<Customer>()
.Where(o => o.Metadata.Localisation == localisation)
.Include(nameof(Customer.SomeLongList))
.GroupBy(x => x.CustomerNumber)
.Select(gr => gr.OrderByDescending(x => x.Metadata.DateAdded).FirstOrDefault())
.ToListAsync();
Short answer:
No way. GroupBy has limitation: after grouping only Key and Aggregation result can be selected. And you are trying to select SomeLongList and full entity Customer.
Best answer:
It can be done by the SQL and ROW_NUMBER Window function but without SomeLongList
Workaround:
It is because it is not effective
var groupingQuery =
from c in DbContext.Set<Customer>()
group c by new { c.CustomerNumber } into g
select new
{
g.Key.CustomerNumber,
DateAdded = g.Max(x => x.DateAdded)
};
var query =
from c in DbContext.Set<Customer>().Include(x => x.SomeLongList)
join g in groupingQuery on new { c.CustomerNumber, c.DateAdded } equals
new { g.CustomerNumber, g.DateAdded }
select c;
var result = await query.ToListAsync();

Find csv data in column from list of data via linq

My column in my table stores a csv list of items. In C# I have a list of items and I want to do a linq query on that column returning all rows that have any of the items in the list.
Table is defined as
Name|Tags
with example data of
Joe | football,soccer,basketball
Mike | hockey,soccer
Steve | basketball,baseball
So if my C# list contains soccer and basketball I would get back Joe and Steve since they both have 1 of those in their tags list.
Note, I'm using entity framework for the table.
You have to bring your entity data to memory:
var result = players.ToList()
.Where(item => item.Tags.Split(',').Any(x => x.Contains("basketball") || x.Contains("baseball"));
Should give you 2 results.
UPDATE: providing a list of sports
List<string> sports = new List<string>
{
"baseball",
"basketball",
"football"
};
var result = players.ToList()
.Where(item => item.Tags.Split(',').Any(sports.Contains));
Say, list is your C# list. csvTable is your table.
var data = from csvItem in csvTable
from tag in csvItem.Tags.split(',')
where list.contains(tag)
select csvItem

Linq operate with aggregate function

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

LINQ queries with many-to-many tables in Entity Data Model

I'm trying to use LINQ to query the following Entity Data Model
based on this db model
I'd like to be able to pull a list of products based on ProductFacets.FacetTypeId.
Normally, I'd use joins and this wouldn't be a problem but I don't quite understand how to query many-to-many tables under the Entity DataModel.
This is an example sql query:
select p.Name, pf.FacetTypeId from Products p
inner join ProductFacets pf on p.ProductId = pf.ProductId
where pf.FacetTypeId in(8, 12)
Presuming EF 4:
var facetIds = new [] { 8, 12 };
var q = from p in Context.Products
where p.FacetTypes.Any(f => facetIds.Contains(f.FacetTypeId))
select p;
In EF (assuming the mapping is done correctly), joins are hardly ever used; navigation properties are used instead.
Your original SQL returns a tuple with repeated Name entries. With LINQ, it's often easier
to "shape" the queries into non-tuple results.
The following should be the same as the SQL, only instead of returning (Name, FacetTypeId) pairs with repeated Names, it will return a type that has a Name and a sequence of FacetTypeIds:
var facetIds = new [] { 8, 12 };
var result = from p in db.Products
select new
{
p.Name,
FacetTypeIds = from pf in p.FacetTypes
where pf.FacetTypeId == 8 || pf.FacetTypeId == 12
select pf.FacetTypeId,
};

Linq query, how to build nested objects from single table

I have a single table and I need to build a bunch of nested objects based on the single table.
Data:
PointA PointB Month Time Price
1 2 11 11:00 10.99
1 2 12 11:00 9.99
Objects are
POINTS {PointA, PointB, Details}
Details {Month, ExtraDetails}
ExtraDetails {Time, Price}
I want to avoid having loads of loops and if statements, so should be able to use linq to do this. but its beyond my linq experience.
edit: These need grouping aswell
any help would be great.
Thanks
Just tried out a solution:
var nestedObjects = from row in data
select new {row.PointA, row.PointB, Details = new {
row.Month, ExtraDetails = new {
row.Time, row.Price
}
}};
This is assuming that you have already got your data into data.
Group by
If you want to group the Points together, you need 'Group By':
var nestedObjects = from row in data
group row by new { row.PointA, row.PointB } into Points
select new {
Points = Points.Key,
Details = from details in Points
select new { row.Month, ExtraDetails = new {
row.Time, row.Price
}}
};
A little more complicated - of course you might want to group by month as well, in which case, you need to follow the same pattern as for the Points bit. Note, this will not create tables, because the group by doesn't quite do that, but it at least creates the structure for you.
Assuming you got your classes defined for the objects you mentioned, and you have a constructor or properties so you can propery create the object in one line you could have a LINQ query returning a list of a POINTS.
If would go something lik this :
var res =
from item in table.AsEnumerable()
select new Points(){PointA = item["PointA"];
PointB = item["PointB"];
Details = from item2 in table.AsEnumberable()
where item["PointA"] = item2["PointA"] and item["PointB"] = item2["PointB"]
select new Details(){
month=item2["month"],
extraDetails = from item3 in table.AsEnumerable()...
}
};
At the end res will be a IEnumerable of Points
I am sorry for the code, I am not at a computer with .NET 3.5 so I cannot write a proper testable query

Resources