Linq Group By with Count and Key Name - c#

I'm attempting to get the company name from a table in which I create the new anonymous type after a group. The query works if I comment out the "CompanyName" Line
db.tbl
.GroupBy(a => a.ID)
.Select(b => new {
// This line is where I need help, I want to grab the company name
CompanyName = b.GroupBy(x=>x.CustomerName).ToString(),
CustomerId = (int) b.Key,
TotalQuotes = b.Count()
})

You don't need to Group each list again.
As I suppose the CustomerName will be the same for all the entities part of a Group, you can simply take the first entity and extract from it the CustomerName:
db.tbl
.GroupBy(a => a.ID)
.Select(c => new {
CompanyName = c.First().CustomerName,
CustomerId = (int) c.Key,
TotalQuotes = c.Count()
});

Related

Order by and group by and sum using SQL

What I am trying to do is get the top 10 most sold Vegetables by grouping them by an Id passed by parameter in a function and ordering them by the sum of their Quantity. I don't know how to use SUM or (total) quite yet but I thought I'd post it here seeking help. If you need me offering you anything else I will be ready.
This is my code:
TheVegLinQDataContext db = new TheVegLinQDataContext();
var query =db.OrderDetails.GroupBy(p => p.VegID)
.Select(g => g.OrderByDescending(p => p.Quantity)
.FirstOrDefault()).Take(10);
And this is an image of my database diagram
Group orders by Vegetable ID, then from each group select data you want and total quantity:
var query = db.OrderDetails
.GroupBy(od => od.VegID)
.Select(g => new {
VegID = g.Key,
Vegetable = g.First().Vegetable, // if you have navigation property
Total = g.Sum(od => od.Quantity)
})
.OrderByDescending(x => x.Total)
.Select(x => x.Vegetable) // remove if you want totals
.Take(10);
Since this is not clear that you are passing what type of id as function parameter, I'm assuming you are passing orderId as parameter.
First apply where conditions then group the result set after that order by Total sold Quantity then apply Take
LINQ query
var result = (from a in orderdetails
where a.OrderId == orderId //apply where condition as per your needs
group a by new { a.VegId } into group1
select new
{
group1.Key.VegId,
TotalQuantity = group1.Sum(x => x.Quantity),
group1.FirstOrDefault().Vegitable
}).OrderByDescending(a => a.TotalQuantity).Take(10);
Lamda (Method) Syntax
var result1 = orderdetails
//.Where(a => a.OrderId == 1) or just remove where if you don't need to filter
.GroupBy(x => x.VegId)
.Select(x => new
{
VegId = x.Key,
x.FirstOrDefault().Vegitable,
TotalQuantity = x.Sum(a => a.Quantity)
}).OrderByDescending(x => x.TotalQuantity).Take(10);

Lambda left join with rows

I have two tables. A table called Order and a table called OrderRows.
An Order can have zero or more OrderRows.
I want to query all Orders and do a Sum for all OrderRows that belong to that Order.
I do that like this:
var model = await _dbContext.Orders
.Join(_dbContext.OrderRows, o => o.Id, or => or.OrderId, (o, or) => new {o, or})
.GroupBy(x => new
{
x.o.Id,
x.o.Name
})
.Select(g => new CustomDto
{
Id = g.Key.Id,
Name = g.Key.Name,
TotalPrice = g.Sum(x => x.wkr.Price)
}).ToListAsync();
This works fine for all Orders that have OrderRows. However, some Orders don't have any OrderRows (yet).
Right now the Orders that don't have any OrderRows, are not included in the result.
In those cases I still want to have them in my result, but with a TotalPrice of 0.
What do I have to change in my Lambda query?
You can use simple Select without grouping. Just calculate TotalPrice as sub-query:
var model = await _dbContext.Orders.Select(o => new CustomDto
{
Id = o.Id,
Name = o.Name,
TotalPrice = _dbContext.OrderRows.Where(or => or.OrderId == o.Id).Sum(or => or.wkr.Price)
}).ToListAsync();
I've not tested it, but hope that idea is clear

C# Convert columns to rows using linq?

I have the following table structure. I want to retrieve value corresponding to key for each group name and insert it store it sequentially in a model class
the table data has been read using a ExecuteQuery and stored in a list of class object, in the below example I will have 4 rows returned. I need to convert it into 2 rows, the column values coming in as rows.
I have the following code written now, But is there any other way to verify it without explicitly checking for say Key == "GroupSpecificProperty1"?
If there is a 3rd category added later, I shouldn't have to modify this code
Result = results.GroupBy(p => p.GroupName )
.Select(g => new FinalModel()
{
GroupName = g.Select(p => p.GroupName ).FirstOrDefault(),
GroupSpecificProperty1 = g.Where(q => q.Key == "GroupSpecificProperty1").Select(v => v.Value).Cast<string>().FirstOrDefault(),
GroupSpecificProperty2= g.Where(q => q.Key == "GroupSpecificProperty2").Select(v => v.Value).Cast<string>().FirstOrDefault(),
}).ToList();
results.GroupBy(p => p.GroupName)
.Select(g => new FinalModel
{
GroupName = g.Key,
Properties = g.ToDictionary(item => item.Key, item=> item.Value)
});
And in the case that for a given GroupName the keys are not unique and you'd want to avoid a "key already exists" exception then you can:
results.GroupBy(p => p.GroupName)
.Select(g => new FinalModel
{
GroupName = g.Key,
Properties = g.GroupBy(item => item.key)
.ToDictionary(innerGroup => innerGroup.Key,
innerGroup => innerGroup.Select(innerItem => innerItem.Value))
});
Then of course you can also replace the outer/inner dictionary with a LookUp if it fits your needs better.

Use LINQ to get comma separated list from different table

I'm using ASP.NET and have a GridView that I want to populate with data from 2 tables. Right now, I have the following query:
var carListData = from x in db.cars
orderby x.carName
select new { x.carId, x.carName, locationIDs = string.Empty };
carList.DataBind();
Now, I want to set locationIDs to be a comma-separated string. I need to look up all the locations that sell each car. I have a table called db.locationCars and it has a locationId column and a carId column. Multiple locations can have the same carId. How do I update my carDataList query to get all the locations that sell each car so those locationIDs can be displayed in the carList gridview?
Thanks for any suggestions!
Using a GroupJoin should work. If the first gives you translation to SQL issues, try the second.
var carListData = db.cars
.GroupJoin(db.locationCars,
(o,i) => o.carId,
(o,i) => i.carId,
(o,i) => new { o.carId, o.carName, locationIds = string.Join(",", i.Select(l => l.locationId)))
.OrderBy(g => g.carName);
Version performing the string join in memory
var carListData = db.cars
.GroupJoin(db.locationCars,
(o,i) => o.carId,
(o,i) => i.carId,
(o,i) => new { o.carId, o.carName, locations = i.Select(l => l.locationId))
.OrderBy(g => g.carName)
.ToList()
.Select(g => new { g.carId, g.carName, locationIds = string.Join(",", g.locations));
foreach( var car in carList )
{
var locationIds = Locations.Where( loc => loc.CarId == car.Id ).Select( loc => loc.Id );
car.locationIDs = string.Join( ",", locationIds );
}
If your foreign keys are defined properly in the database, and your entity framework model his updated with those foreign keys. Then you would have a collection of locationCars in each Car object with the locationIds you need.
Create a new object called carListData and select into that object. This object can convert your collection of LocationCars into a csv of locationIds.

Use Linq to return first result for each category

I have a class (ApplicationHistory) with 3 properties:
ApplicantId, ProviderId, ApplicationDate
I return the data from the database into a list, however this contains duplicate ApplicantId/ProviderId keys.
I want to supress the list so that the list only contains the the earliest Application Date for each ApplicantId/ProviderId.
The example below is where I'm currently at, but I'm not sure how to ensure the earliest date is returned.
var supressed = history
.GroupBy(x => new
{
ApplicantId = x.ApplicantId,
ProviderId = x.ProviderId
})
.First();
All advice appreciated.
Recall that each group formed by the GroupBy call is an IGrouping<ApplicationHistory>, which implements IEnumerable<ApplicationHistory>. Read more about IGrouping here. You can order those and pick the first one:
var oldestPerGroup = history
.GroupBy(x => new
{
ApplicantId = x.ApplicantId,
ProviderId = x.ProviderId
})
.Select(g => g.OrderBy(x => x.ApplicationDate).FirstOrDefault());
You are selecting first group. Instead select first item from each group:
var supressed = history
.GroupBy(x => new {
ApplicantId = x.ApplicantId,
ProviderId = x.ProviderId
})
.Select(g => g.OrderBy(x => x.ApplicationDate).First());
Or query syntax (btw you don't need to specify names for anonymous object properties in this case):
var supressed = from h in history
group h by new {
h.ApplicantId,
h.ProviderId
} into g
select g.OrderBy(x => x.ApplicationDate).First();

Categories