group and sum in linq to sql - c#

I wanted to sum the DayDifference based on studentId
List<Timeliness> studentData = (from redt in RoundEndDateTable.AsEnumerable()
join st in Activity on redt.Field<string>("strActivityName") equals st.ActivityName
orderby (DateTime.Parse(redt.ItemArray[1].ToString()) - DateTime.Parse(st.RewardType.ToString())) descending
select new Timeliness
{
DayDifference = int.Parse((DateTime.Parse(redt.ItemArray[1].ToString()) - DateTime.Parse(st.RewardType)).ToString().Split('.')[0]),
StudentId = st.AssociateId.ToString()
}).ToList();
I tried the below code
var groupedCustomerList = studentData
.GroupBy(u => u.StudentId)
.Select(grp => grp.ToList())
.ToList();
Not sure where to add the sum in the above codes
In the first query i am getting student id repeated
ex:
[0] - studentid:123 DayDifference:16
[1] - studentid:123 DayDifference:8
[2] - studentid:121 DayDifference:16
I need
[0] - studentid:123 DayDifference:24
[2] - studentid:121 DayDifference:16

This should create a collection of anonymous types, containing each "Key" (StudentId), and the sum of the "DayDifference" values for each StudentId:
var result =
studentData.GroupBy(u => u.StudentId)
.Select(grp => new { Id = grp.Key, TotalDiff = grp.Sum(x => x.DayDifference) })
.ToList();

Related

LINQ: Group items, then select Min and finally select Max

I have below list of items:
ID Date
01200 11/11/2020
01200 11/11/2021
02100 01/01/2019
02100 01/01/2029
I am trying to group items by ID, then for each group select the item which has the Max date, and finally select the item which has the Min date. Taken into account above set of items, the final result would be 11/11/2021.
So I have implemented two ways here to do the same thing. Option 1 is working but option 2 isn't.
step-by-step:
// first get the groups:
var groups = items.GroupBy(i => i.ID);
// find the max date in each group
var maxDates = groups.Select(g => g.OrderByDescending(i => i.Date).First());
// now find the earliest max date
var minDate = maxDates.OrderBy(i => i.Date).First();
combined into one line:
var minDate = items.GroupBy(i => i.ID)
.Select(g => g.OrderByDescending(i => i.Date).First())
.OrderBy(i => i.Date).First();
...
GroupBy(p => p.id,
p => p.date,
(key, g) => new { id = key, date = g.Max() })
returns an IEnumerable of an anonymous type. You cannot convert anonymous type to type Foo via .ToList<Foo>.
You should rewrite you code to resolve compilation error as
var res2 = cv.GroupBy(
p => p.id,
p => p.date,
(key, g) => new Foo{ id = key, date = g.Max() //add here class name Foo
}).Aggregate((u,v) => u.date < v.date ? u: v);
EDIT: or if you not use Dump() to show result then you may use anonymous type in GroupBy() like:
var res2 = cv.GroupBy(
p => p.id,
p => p.date,
(key, g) => new { id = key, date = g.Max() }).Aggregate((u,v) => u.date < v.date ? u: v);
Also you may use #D Stanley idea to find Foo object like:
var minDate = cv.GroupBy(i => i.id,
p => p.date,
(key, g) => new Foo() { id = key, date = g.Max() }).OrderBy(e=>e.date).First();

LINQ Query with GroupBy, MAX and Count

What could be the LINQ query for this SQL?
SELECT PartId, BSId,
COUNT(PartId), MAX(EffectiveDateUtc)
FROM PartCostConfig (NOLOCK)
GROUP BY PartId, BSId
HAVING COUNT(PartId) > 1
I am actually grouping by two columns and trying to retrieve max EffectiveDateUtc for each part.
This is what I could write. Stuck up on pulling the top record based on the date.
Also not sure, if this is a optimal one.
//Get all the parts which have more than ONE active record with the pat
//effective date and for the same BSId
var filters = (from p in configs
?.GroupBy(w => new
{
w.PartId,
w.BSId
})
?.Select(g => new
{
PartId = g.Key.PartId,
BSId = g.Key.BSId,
Count = g.Count()
})
?.Where(y => y.Count > 1)
select p)
?.Distinct()?.ToList();
var filteredData = (from p in configs
join f in filters on p.PartId equals f.PartId
select new Config
{
Id = p.Id,
PartId = p.PartId,
BSId = p.BSId,
//EffectiveDateUtc = MAX(??)
}).OrderByDescending(x => x.EffectiveDateUtc).GroupBy(g => new { g.PartId, g.BSId }).ToList();
NOTE: I need the top record (based on date) for each part. Was trying to see if I can avoid for loop.
The equivalent query would be:
var query =
from p in db.PartCostConfig
group p by new { p.PartId, p.BSId } into g
let count = g.Count()
where count > 1
select new
{
g.Key.PartId,
g.Key.BSId,
Count = count,
EffectiveDate = g.Max(x => x.EffectiveDateUtc),
};
If I understand well, you are trying to achieve something like this:
var query=configs.GroupBy(w => new{ w.PartId, w.BSId})
.Where(g=>g.Count()>1)
.Select(g=>new
{
g.Key.PartId,
g.Key.BSId,
Count = g.Count(),
EffectiveDate = g.Max(x => x.EffectiveDateUtc)
});

How to filter a list based on 2 properties?

I have a list in my code that I need to filter through and return specific rows based on two criteria. The List in question is a list of models from a database. There are two ID properties on each model, one is the ID from the data table and is unique, the other is an ID we use to identify groups and can repeat. We'll call them ID and GroupID. Basically, I want the resulting list to have only one of each GroupID, and it should be the one with the highest (numerically speaking) ID. For example:
Input:
List<MyModel> modelList = new List<MyModel>
modelList[0].ID = 1 modelList[0].GroupID = 5
modelList[1].ID = 2 modelList[1].GroupID = 5
modelList[2].ID = 3 modelList[2].GroupID = 6
modelList[3].ID = 4 modelList[3].GroupID = 6
Desired Output:
Models at indexes 1 and 3.
Using LINQ:
var items = (from model in modelList
group model by model.GroupID into modelGroup
select modelGroup.Max(i => i.ID)).ToList();
What you have to do here is first order the modelList by ID and then GroupBy the list items by GroupID, then pull the item with max Id value.
var result = modelList.OrderByDescending(x => x.ID).GroupBy(x => x.GroupID).Select(x => x.First());
the above query will give you the result.
This is your solution:
var myData = models.GroupBy(model => model.GroupId)
.Select(group => group.OrderByDescending(model => model.Id).First());
Or you could also do this:
var myData = models.GroupBy(model => model.GroupId)
.Select(group => group.First(model => model.Id == group.Max(model1 => model1.Id)));
For fun, here's a fiddle.
You can try to use GroupBy.
var q = modelList.GroupBy(x => x.GroupID, x => x,
(key, g) => new {
GroupID = key,
Id = g.Max(c => c.ID)
});
This should group all your elements by GroupId and select Max ID in one of that groups.
Try this code:
List<MyModel> modelList = new List<MyModel>();
modelList.Add(new MyModel());
modelList.Add(new MyModel());
modelList.Add(new MyModel());
modelList.Add(new MyModel());
modelList[0].ID = 1; modelList[0].GroupID = 5;
modelList[1].ID = 2; modelList[1].GroupID = 5;
modelList[2].ID = 3; modelList[2].GroupID = 6;
modelList[3].ID = 4; modelList[3].GroupID = 6;
var list = from ml in modelList group ml by ml.ID into r select new { ID = r.Key, MaxGroupID = r.Max() };
this might help you
modelList.GroupBy(model => model.GroupId, g => g.Id).Select(item => item.Max())
var newModelList = modelList.GroupBy(ml => ml.GroupID)
.Select(g => new MyModel
{
ID = g.OrderByDescending(x => x.ID).First().ID,
GroupID = g.Key
}).ToList();
Details
1) GroupBy then Select to get distinct items over GroupID.
2) First() after OrderByDescending to get highest ID.
3) new MyModel in Select is just to be explicit about the projection.

I want to print Count using Groupby and Left join in linq

I having two list or table as per below:
Query:
var q = db.tbl_User_to_CustomerMast
.Where(i => i.fk_Membership_ID == m.MembershipID)
.Join(
db.tbl_CustomerMast,
u => u.fk_Customer_ID,
c => c.CustomerID,
(u, c) => new { UserCustomer = u, Customer = c })
.Where(i => i.UserCustomer.fk_Store_ID == shopid).ToList();
Output:
List A:
User_Customer_ID Name
===================================
1 XYZ
2 ABC
Query:
var rewards = q.Join(
db.tbl_RewardAwardMast,
i => i.UserCustomer.User_Customer_ID,
j => j.fk_Customer_UserID,
(i, j) => new { Customer = i, Reward = j })
.Where(i => i.Reward.RewardDate >= i.Customer.UserCustomer.Membership_Start)
.GroupBy(i => i.Reward.fk_Customer_UserID)
.Select(i => new { CustomerID = i.Key, RewardCount = i.Count()})
.ToList();
Output:
List B:
User_Customer_ID RewardCount
===================================
1 5
Here is final Output Table
User_Customer_ID Name RewardCount
===============================================
1 XYZ 5
2 ABC 0
If I want to check that which user_customer_ID has less than 5 Reward Count, How I will Check:
Query:
var final = q.GroupJoin(
rewards,
i => i.UserCustomer.User_Customer_ID,
j => j.CustomerID,
(i, j) => new { Customer = i, Reward = j.DefaultIfEmpty() })
.Select(i => new { Count = i.Reward, id = i.Customer.UserCustomer.User_Customer_ID })
.ToList();
var final1 = final.Where(i => i.Count < m.MembershipMinVisit.Value).ToList();
Error:
Operator '<' cannot be applied to operands of type 'System.Collections.Generic.IEnumerable' and 'int'
You don't need a group join here as for each customer you need a single result (reward). Also because you need only customers with rewards < 5, an inner join using that condition wil give you what you want:
var final = q.Join( // Join instead of GroupJoin
rewards.Where(r => r.RewardCount < 5), // filter out rewards >= 5
i => i.UserCustomer.User_Customer_ID,
j => j.CustomerID,
(i, j) => new { Customer = i, Reward = j })
.Select(i => new {
Reward = i.Reward, // 'Count' is a bad name
// it is still the reward object
id = i.Customer.UserCustomer.User_Customer_ID
})
.ToList();
In your original query, Count (bad name) is a collection (IEnumerable) of awards, that's why you get that error. To fix it, you have to check that the single returned reward is not null (to filter out users without rewards at all, because you use a left join) and that it has a RewardCount less that 5:
var final1 = final.Where(i => i.Count.Single() != null &&
i.Count.Single().RewardCount < 5)
.ToList();

LINQ query with distinct count

I am trying to construct a LINQ query in C# that will give me a list of distinct values from a column in a dataset with a count for each row. The results would look like this.
State Count
AL 55
AK 40
AZ 2
Here is the SQL that does that.
SELECT name, COUNT(*) AS count
FROM architecture arch
GROUP BY name
ORDER BY name
I've figured out the LINQ to get the DISTINCT values which is.
var query = ds.Tables[0].AsEnumerable()
.OrderBy(dr1 => dr1.Field<string>("state"))
.Select(dr1 => new {state = dr1.Field<string>("state")})
.Distinct().ToList();
But I can't figure out how to get the COUNT(*) for each distinct value to work in LINQ. Any idea how I can add that into the LINQ query?
You need to group your results based on State and the Select count from the group like:
var query = ds.Tables[0].AsEnumerable()
.GroupBy(r => r.Field<string>("state"))
.Select(grp => new
{
state = grp.Key,
Count = grp.Count()
})
.OrderBy(o => o.state)
.ToList();
Group all rows by value of state column. Then order groups by grouping key. And last step - project each group into anonymous object with grouping key (state) and count of rows in group:
var query = ds.Tables[0].AsEnumerable()
.GroupBy(r => r.Field<string>("state"))
.OrderBy(g => g.Key)
.Select(g => new { State = g.Key, Count = g.Count() })
.ToList();
Query syntax will look like (I'll skip converting to list, to avoid mixing syntaxes):
var query = from r in ds.Tables[0].AsEnumerable()
group r by r.Field<string>("state") into g
orderby g.Key
select new {
State = g.Key,
Count = g.Count()
};
I think you need GroupBy
var query = ds.Tables[0].AsEnumerable()
.GroupBy(dr1 => dr1.Field<string>("state"))
.Select(g => new {state = g.Key, count = g.Count())
.ToList();
Why bother with Distinct, when you can translate your SQL query to LINQ almost word-for-word? You can do it like this:
var query = ds.Tables[0].AsEnumerable()
.GroupBy(dr1 => dr1.Field<string>("state"))
.Select(g => new {
State = g.Key
, Count = g.Count()
})
.OrderBy(p => p.State)
.ToList();
This produces a list of {State, Count} pairs. If you prefer a dictionary of state-to-count, you can change your query like this:
var query = ds.Tables[0].AsEnumerable()
.GroupBy(dr1 => dr1.Field<string>("state"))
.ToDictionary(g => g.Key, g => g.Count());
var query = ds.Tables[0].AsEnumerable()
.GroupBy(x=>x.Field<string>("state"))
.Select( g => new{
state = g.Key,
count = g.Count()
});
Guess what, the equivalent of group by is group by :)
var query = from dr1 in ds.Tables[0].AsEnumerable()
group dr1 by dr1.Field<string>("state") into state
select new { State = state.Key, Count = state.Count() };
var stat = from row in ds.Tables[0].AsEnumerable()
group row by new
{
Col1 = row["Name"],
} into TotalCount
select new
{
ActionName = TotalCount.Key.Col1,
ActionCount = TotalCount.Count(),
};

Categories

Resources