Linq Query with SUM and ORDER BY - c#

I have a (C#) class called Hit with an ItemID (int) and a Score (int) property. I skip the rest of the details to keep it short. Now in my code, I have a huge List on which I need to do the following select (into a new List): I need to get the sum of all Hit.Score's for each individual Hit.ItemID, ordered by Score. So if I have the following items in the original list
ItemID=3, Score=5
ItemID=1, Score=5
ItemID=2, Score=5
ItemID=3, Score=1
ItemID=1, Score=8
ItemID=2, Score=10
the resulting List should contain the following:
ItemID=2, Score=15
ItemID=1, Score=13
ItemID=3, Score=6
Can somebody help?

var q = (from h in hits
group h by new { h.ItemID } into hh
select new {
hh.Key.ItemID,
Score = hh.Sum(s => s.Score)
}).OrderByDescending(i => i.Score);

IEnumerable<Hit> result = hits.
GroupBy(hit => hit.ItemID).
Select(group => new Hit
{
ItemID = group.Key,
Score = group.Sum(hit => hit.Score)
}).
OrderByDescending(hit => hit.Score);

Related

Return all fields, Group by And Aggregate functions in lambda

I have such a table as shown below
I need to group each record by LifePluseCaseId column,
and each group must be selected by Min distance and Min Duration
And return All of the fields.
I have tried this:
var query = db.ApplicantCenterDistance.GroupBy(s => s.LifeplusCaseId)
.Select(s => new {
Id = s.Key,
MinDistance = s.Min(m => m.Distance),
Duration = s.Min(m => m.Duration)
}).ToList();
But i don't know how to get all fields in Select statement and what the role of key is.
Is s.Key equals to s.LifeplusCaseId?
From the above queries, each group will have multiple ApplicantCenterDistances record (in theory) because... well it's a group. If you really want to get all the items in each group as well, you can do like this (pseudo-code):
var productByCategory = await db.Products
.GroupBy(q => q.CategoryId)
.Select(q => new {
CategoryId = q.Key,
// Here q is also acting as a list of products with the same `CategoryId`
Products = q,
// Or if you only want some specific fields
ProductCustoms = q.Select(p => new {
Name = p.Name,
Color = p.Color,
// All fields you want
})
}).ToListAsync(); // Do whatever you want with the result
Now it makes much more sense right? The productByCategory is a list of groups, each group has the key (CategoryId), and has a list of products that has that matching CategoryId.

Highhest Number's key in a GroupBy

I have a simple class:
class Balls
{
public int BallType;
}
And i have a really simple list:
var balls = new List<Balls>()
{
new Balls() { BallType = 1},
new Balls() { BallType = 1},
new Balls() { BallType = 1},
new Balls() { BallType = 2}
};
I've used GroupBy on this list and I want to get back the key which has the highest count/amount:
After I used x.GroupBy(q => q.BallType) I tried to use .Max(), but it returns 3 and I need the key which is 1.
I also tried to use Console.WriteLine(x.GroupBy(q => q.Balltype).Max().Key); but it throws System.ArgumentException.
Here's what I came up with:
var mostCommonBallType = balls
.GroupBy(k => k.BallType)
.OrderBy(g => g.Count())
.Last().Key
You group by the BallType, order by the count of items in the group, get the last value (since order by is in an ascending order, the most common value would be the last) and then return it's key
Some came up with the idea to order the sequence:
var mostCommonBallType = balls
.GroupBy(k => k.BallType)
.OrderBy(g => g.Count())
.Last().Key
Apart from that it is more efficient to OrderByDescending and then take the FirstOrDefault, you also get in trouble if your collection of Balls is empty.
If you use a different overload of GroupBy, you won't have these problems
var mostCommonBallType = balls.GroupBy(
// KeySelector:
k => k.BallType,
// ResultSelector:
(ballType, ballsWithThisBallType) => new
{
BallType = ballType,
Count = ballsWithThisBallType.Count(),
})
.OrderByDescending(group => group.Count)
.Select(group => group.BallType)
.FirstOrDefault();
This solves the previously mentioned problems. However, if you only need the 1st element, why would you order the 2nd and the 3rd element? Using Aggregate instead of OrderByDescending will enumerate only once:
Assuming your collection is not empty:
var result = ... GroupBy(...)
.Aggregate( (groupWithHighestBallCount, nextGroup) =>
(groupWithHighestBallCount.Count >= nextGroup.Count) ?
groupWithHighestBallCount : nextGroup)
.Select(...).FirstOrDefault();
Aggregate takes the first element of your non-empty sequence, and assigns it to groupWithHighestBallCount. Then it iterates over the rest of the sequence, and compare this nextGroup.Count with the groupWithHighestBallCount.Count. It keeps the one with the hightes value as the next groupWithHighestBallCount. The return value is the final groupWithHighestBallCount.
See that Aggregate only enumerates once?

Orderby Not working after grouping items

I have a function that's main goal to group items by id, count them and finally order them by count.
var Db = list.Select(x => new
{
Id = x
});
var groups = Db.GroupBy(g => new { Id = g.Id })
.Select(g => new
{
Id = g.Key.Id,
Cnt = g.Count()
}).Distinct().OrderBy(g => g.Cnt).ToList();
Do you have any idea, where the error might be? It is not ordering. I have a bunch of low counts at first then it becomes random.
'list' is a one-dimensional list with many duplicated Ids (string) .
This is the output its giving out: ( I do not understand why there's duplicates in the ID since I'm only grouping by one key).
Link to output : https://pastebin.com/NjqJ4Aub

How to c# List<> order by and Group by according to parameters?

I have a class and its List
abc cs = new abc();
List<abc> Lst_CS = new List<abc>();
and I set some value by HidenField in foreach loop
foreach (blah blah)
{
cs = new abc{
No = VKNT,
GuidID=hdnGuidID.Value.ToString(),
RecID=hdnRecID.Value.ToString(),
Date=HdnDate.Value.ToString()
};
Lst_CS.Add(cs);
}
and finally I get a List_CS and I order by Lst_CS according to Date like this;
IEnumerable<abc> query = Lst_CS.OrderBy(l => l.Date).ToList();
but in extra, I want to group by according to No.
Briefly, I want to order by Date and then group by No on Lst_CS How can I do ?
Thanks for your answer
Well you just just do the ordering then the grouping like so:
Lst_CS.OrderBy(l => l.Date)
.GroupBy(l => l.No)
.ToList();
Each list of items in each group will be ordered by date. The groupings will be in the order that they are found when the entire list is ordered by date.
Also your ForEach can be done in one Linq statement, then combined with the ordering and grouping:
var query = blah.Select(b => new abc{
No = VKNT,
GuidID=hdnGuidID.Value.ToString(),
RecID=hdnRecID.Value.ToString(),
Date=HdnDate.Value.ToString()
})
.OrderBy(l => l.Date)
.GroupBy(l => l.No)
.ToList();

Using LINQ on ObservableCollection with GroupBy and Sum aggregate

I have the following block of code which works fine;
var boughtItemsToday = (from DBControl.MoneySpent
bought in BoughtItemDB.BoughtItems
select bought);
BoughtItems = new ObservableCollection<DBControl.MoneySpent>(boughtItemsToday);
It returns data from my MoneySpent table which includes ItemCategory, ItemAmount, ItemDateTime.
I want to change it to group by ItemCategory and ItemAmount so I can see where I am spending most of my money, so I created a GroupBy query, and ended up with this;
var finalQuery = boughtItemsToday.AsQueryable().GroupBy(category => category.ItemCategory);
BoughtItems = new ObservableCollection<DBControl.MoneySpent>(finalQuery);
Which gives me 2 errors;
Error 1 The best overloaded method match for 'System.Collections.ObjectModel.ObservableCollection.ObservableCollection(System.Collections.Generic.List)' has some invalid arguments
Error 2 Argument 1: cannot convert from 'System.Linq.IQueryable>' to 'System.Collections.Generic.List'
And this is where I'm stuck! How can I use the GroupBy and Sum aggregate function to get a list of my categories and the associated spend in 1 LINQ query?!
Any help/suggestions gratefully received.
Mark
.GroupBy(category => category.ItemCategory); returns an enumerable of IGrouping objects, where the key of each IGrouping is a distinct ItemCategory value, and the value is a list of MoneySpent objects. So, you won't be able to simply drop these groupings into an ObservableCollection as you're currently doing.
Instead, you probably want to Select each grouped result into a new MoneySpent object:
var finalQuery = boughtItemsToday
.GroupBy(category => category.ItemCategory)
.Select(grouping => new MoneySpent { ItemCategory = grouping.Key, ItemAmount = grouping.Sum(moneySpent => moneySpent.ItemAmount);
BoughtItems = new ObservableCollection<DBControl.MoneySpent>(finalQuery);
You can project each group to an anyonymous (or better yet create a new type for this) class with the properties you want:
var finalQuery = boughtItemsToday.GroupBy(category => category.ItemCategory);
.Select(g => new
{
ItemCategory = g.Key,
Cost = g.Sum(x => x.ItemAmount)
});
The AsQueryable() should not be needed at all since boughtItemsToday is an IQuerable anyway. You can also just combine the queries:
var finalQuery = BoughtItemDB.BoughtItems
.GroupBy(item => item.ItemCategory);
.Select(g => new
{
ItemCategory = g.Key,
Cost = g.Sum(x => x.ItemAmount)
});

Categories