How to use GroupBy when we do it for the collection? - c#

I have the following code:
private IDictionary<Guid, JobStatistic> GetAgentsStatistics(IList<Guid> agentIds, DateTime startTime, DateTime endTime)
{
...
IDictionary<Guid, JobStatistic> jobStatistics = reportData.GroupBy(item => item.AgentId)
.ToDictionary(items => items.Key, items => items.ToJobsStatistic());
// Some agents might not have any records in specified period of time
var missingAgents = agentIds.Except(jobStatistics.Keys);
missingAgents.ForEach(agentId => jobStatistics.Add(agentId, new JobStatistic()));
return jobStatistics;
}
But now I change my DataContract (report data) and I need to use item.AgentIds instead of item.AgentId. How to change the code to do the same thing but with a collection of elements?

I would probably use a different form of LINQ. I think something like this could work:
var jobStatisticsResult = from data in reportData
from agentId in data.AgentIds
group data by agentId
into grp
select new { AgentId = grp.Key, Items = grp.ToList() };
var jobStatistics = jobStatisticsResult.ToDictionary(x => x.AgentId, x => x.Items.ToJobsStatistic());

Related

EF core get data from entity per day by limit

This is an entity
public class ViewModel
{
public string Id { get; set; }
public DateTime Start { get; set; }
public string Name { get; set; }
}
This is my context query,works with ef core in dbcontext.
var list = _Service.GetDataByMonth(start,end).ToList();
// it returns all entity between giving start, end param.
// start and end is Datetime property comes from ajax. This code works fine it return almost 1k objectlist with type of ViewModel
like
[0] Id="1",Title="sample",Start:""15.12.2020"
[1] Id="2",Title="sample2",Start:""15.12.2020"
[2] Id="3",Title="sample3",Start:""16.12.2020"
[3] Id="4",Title="sample4",Start:""16.12.2020"
As shows above we got almost 20+ entity per day.
I can get count per day like
var listt = _Service.GetDataByMonth(start,end).GroupBy(x => x.Start.Date).Select(grp => new { Date = grp.Key, Count = grp.Count() });
[0] Key:""15.12.2020",Count:20
[1] Key:""16.12.2020",Count:25
[2] Key:""17.12.2020",Count:44
it returns like this.
So what i want is giving start and end param a funciton then get 3 values per day between giving datetime object
NEW
var list1= _Service.GetDataByMonth(start,end).GroupBy(x => x.StartDate.Date)
.Select(grp => grp.Take(3)).ToList();
//this type List<Ienumerable<Viewmodel>>
var list2 = _Service.GetDataByMonth(start,end).GroupBy(x => x.StartDate.Date).Select(grp => grp.Take(3).ToList()).ToList();
// this type List<List<Viewmodel>>
My want it List<Viewmodel>
service
...
return entity.Where(x =>
x.IsDeleted == false &&
(x.StartDate.Date >= start.Date && x.StartDate.Date <=end.Date)
).OrderBy(x => x.FinishDate).ToList();
// it work with this way but bad way
var lis = list.SelectMany(d => d).ToList();
Yes I figuredout using selectmany instead of select works fine. Thank you again
You can use .Take() to only take 3 items of each group:
_Service.GetDataByMonth(start,end)
.GroupBy(x => x.Start.Date)
.Select(grp => new { Data = grp.Take(3).ToList(), Date = grp.Key, Count = grp.Count() })
.ToList();
var list1= _Service.GetDataByMonth(start,end).GroupBy(x => x.StartDate.Date)
.Select(grp => grp.Take(3)).ToList();
It returns List<List> maybe it will help some one bu my need is comes from with this code
This code makes list of list each element is list and contians 3 object.
var list1= _Service.GetDataByMonth(start,end).GroupBy(x => x.StartDate.Date)
.SelectMany(grp => grp.Take(3)).ToList();
Many makes just list. It mades list with ordered objects

LINQ group by datetime and sum

I have a List of orders purchased on specific date. I need to group them all into one date ( purchases made on same day) and then sum them.. I made a class which looks like this:
public class StoreAnalyticOrders
{
public DateTime OrderDate { get; set; }
public float AmountPaid { get; set; }
}
And then the I fill the list which is called "GraphList" with results...
The LINQ that I tried to perform what I just described up there is:
var result = GraphList
.GroupBy(l => l.OrderDate)
.Select(cl => new StoreAnalyticOrders
{
AmountPaid = cl.Sum(c => c.AmountPaid),
}).ToList();
But for some reason the dates are in bad format (they are lost) and they show up like this:
1/1/0001 12:00:00 AM
and this is the previous format of the Order date property:
11/21/2016 6:05:32 AM
What am I doing wrong here?
Edit: #Ivan Stoev is this what you ment:
var result = GraphList
.GroupBy(l => l.OrderDate.Date)
.Select(cl => new StoreAnalyticOrders
{
OrderDate = cl.Key,
AmountPaid = cl.Sum(c => c.AmountPaid)
}).ToList();
You can not use make changes to datetime in a linq to entities query, and you should use DbFunctions:
GraphList.GroupBy(gl => DbFunctions.TruncateTime((DateTime)gl.Datetime)).
Select(gl=> new{
Date = DbFunctions.TruncateTime((DateTime)gl.FirstOrDefault().Datetime),
TotalAmountPaid = gl.Sum(x=> x.AmountPaid)
}).ToList();

Linq Groupby How to Sum Different 4 Model Values

I've three model properties like:
public decimal Date{ get; set; }
public decimal Money{ get; set; }
public string CityId{ get; set; } // grouping property
I need to group by "CityId" than i want to select date and money grouped by "CityId".
Code for this:
var results = model.Select(x => new MyViewModel(){
Date= x.Date,
Money= x.Money,
CityId= x.CityId,
}).GroupBy(x=>x.CityId)
.ToList();
I can get what i want with this Linq query. Than i want to match up my values with this code:
for (int i = 0; i < model.Count; i++)
{
NewList myList = new NewList();
myList.Date= results[i]. // i cant get my properties(Date,Money, CityId)
myList.Money= results[i]. // i cant get my properties(Date,Money, CityId)
myList.CityId= results[i]. // i cant get my properties(Date,Money, CityId)
}
After spelling hours, what's the wrong with this code? How can i get Date, Money, CityId properties?
GroupBy returns groupings of items, an object than contains all grouped items and a key which this group belongs to. So currently your results is of type that looks something like List<int, IGrouping<MyViewModel>>. What you are looking for is apparently List<MyViewModel>, for which you can use SelectMany:
var results = model.Select(x => new MyViewModel()
{
Date= x.Date,
Money= x.Money,
CityId= x.CityId,
})
.GroupBy(x=>x.CityId)
.SelectMany(x => x)
.ToList();
This should flatten your groupings into a single list, with items ordered in groups.
Update. Note that the code above assumes that you are using GroupBy intentionally. As pointed out in comments, if this is the only use case you have, then simple OrderBy might be a better alternative:
var results = model.Select(x => new MyViewModel()
{
Date= x.Date,
Money= x.Money,
CityId= x.CityId,
})
.OrderBy(x => x.CityId)
.ToList();

LINQ projection combining date and list

I have a list of objects containg value and date of transaction.
DateTime Date { get ; set; }
double Value { get; set; }
I want to get the new grouped object which will contain date of transaction and list of values for this particular day.
I can retrieve both list and date but i dont know how to use projection to cast them into new object.
var res1 = ExpenseList.Where(p => p.Date == Convert.ToDateTime("01-12-2013"))
.Select(p => p.Value)
.ToList();
DateTime res2 = ExpenseList.Where(p => p.Date == Convert.ToDateTime("01-12-2013"))
.Select(p => p.Date)
.FirstOrDefault();
There might have been some confusion around I will post pictures to clarify
I have something like this in ExpenseList set of date and value
I want one Date and Collection of Values
To group all of the values for a particular day you'll want to use GroupBy
var groups = ExpenseList.GroupBy(expense => expense.Date,
expense => expense.Value);
ExpenseList.Where(p => p.Date == Convert.ToDateTime("01-12-2013"))
.Select(p => new MyObject {Date = p.Date, Value = p.Value})
.ToList();
Replace MyObject for the actual type you have defined.

How do i sum a list of items by code(or any field)?

I have an object that has a list of another object in it. i.e Object1 contains List<Object2>.
Assuming this is the definition of object 2:
public class Object2
{
string code,
string name,
decimal amount
}
I want to be a able to make a list2 from the list whose value will contain what something similar to what a select name, code, sum(amount) group by code kinda statement could have given me
this is what i did but it didnt contain what i needed on passing through.
var newlist = obj2List.GroupBy(x => x.code)
.Select(g => new { Amount = g.Sum(x => x.amount) });
I want code and name in the new list just like the sql statement above.
You're almost there:
var newlist = obj2List.GroupBy(x => x.code)
.Select(g => new
{
Code = g.First().code,
Name = g.First().name,
Amount = g.Sum(x => x.amount)
});
This groups the items by code and creates an anonymous object for each group, taking the code and name of first item of the group. (I assume that all items with the same code also have the same name.)
If you are grouping by code and not by name you'd have to choose something for name from the list, perhaps with First() or Last() or something.
var newlist = obj2List.GroupBy(x => x.code).Select(g => new {
Code = g.Key,
Name = g.First().name,
Amount = g.Sum(x => x.amount)
});
var query = Object1.Obj2List
.GroupBy(obj2 => obj2.code)
.Select(g => new {
Names = string.Join(",", g.Select(obj2.name)),
Code = g.Key,
Amount = g.Sum(obj2 => obj2.Amount)
});
Since you group by code only you need to aggregate the name also in some way. I have used string.Join to create a string like "Name1,Name2,Name3" for each code-group.
Now you could consume the query for example with a foreach:
foreach(var x in query)
{
Console.WriteLine("Code: {0} Names: {1} Amount: {2}"
, x.Code, x.Names, x.Amount);
}
Instead of using the LINQ Extension Methods .GroupBy() and .Select() you could also use a pure LINQ statement which is way easier to read if you come from a SQL Background.
var ls = new List<Object2>();
var newLs = from obj in ls
group obj by obj.code into codeGroup
select new { code = codeGroup.Key, amount = codeGroup.Sum(s => s.amount) };

Categories

Resources