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();
Related
I construct the below query to display data from Json.
Assume this query returns 50 rows. But I want only first 10 rows to be displayed.
What is the equivalent of select top 10 or limit 10 in this scenario. Thank you.
List<Item> result = items.GroupBy(x => x.ItemNo)
.Select(x => new Item
{
ItemNo = x.Key,
ItemName = x.FirstOrDefault(y => y.Key == "ItemName")?.Value,
Date = x.FirstOrDefault(y => y.Key == "Date")?.Value
}).OrderByDescending(y => y.date)
.ToList();
In any query into your data pipeline you should NEVER request the lot. Why? You have no idea how many records there are in a query. You may think there are only a few but if a DBAdmin has done something wrong, there may be 10 million rows in the customer table.
So use a Request query object to request your data with some boundary condition values set by default.
public record ListQueryRequest {
public int StartIndex { get; init; } = 0;
public int PageSize { get; init; } = 1000;
}
Your data pipeline service can then do something like this:
private async ValueTask<ListRequestResult<TRecord>> GetItemsAsync<TRecord>(ListQueryRequest listQuery)
{
using var dbContext = this.factory.CreateDbContext();
dbContext.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
IQueryable<TRecord> query = dbContext.Set<TRecord>();
/.... any IQueryable conditions such as filtering or ordering
query = query
.Skip(listQuery.StartIndex)
.Take(listQuery.PageSize);
// delayed execution of the query
var list = await query.ToListAsync();
// build and return your request result
}
Where the result object template looks like this:
public record ListQueryResult<TRecord>
{
public bool Successful {get; init;}
public string Message {get; init;}
public IEnumerable<TRecord> Items {get; init;}
}
I think that the method you are looking for is Take
Take method from Microsoft website
In the end I think your code should look like this:
List<Item> result = items.GroupBy(x => x.ItemNo)
.Select(x => new Item
{
ItemNo = x.Key,
ItemName = x.FirstOrDefault(y => y.Key == "ItemName")?.Value,
Date = x.FirstOrDefault(y => y.Key == "Date")?.Value
}).OrderByDescending(y => y.date).Take(10).ToList();
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
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();
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());
I have a list of string that i want to order by quantity. The List contain a list of Order-CreationDate with datetime values. I'm converting this values to strings as as i will need that for later.
My current output is a list of CreationDate that looks like this.
2014-04-05
2014-04-05
2014-04-05
2014-04-05
2014-04-06
2014-04-06
2014-04-06
...
I get a list of dates as expected but i want to group number of dates by the date. This mean i need another variable with number of total orders. Ive tried creating a new variable, using a for loop and linq queries but not getting the results I want.
How can I get number of orders by CreationDate? I need to count total number of orders by CreationDate but I can't find a way to do this.
The expected output would be:
2014-04-05 4 - representing 4 orders that date
2014-04-06 3 - representing 3 orders that date.
This what my code looks like:
List<string> TotalOrdersPaid = new List<string>();
foreach (var order in orders)
{
if (order.PaymentStatus == PaymentStatus.Paid)
{
string Created = order.CreatedOnUtc.Date.ToString("yyyy-MM-dd");
order.CreatedOnUtc = DateTime.ParseExact(Created, "yyyy-MM-dd", CultureInfo.InvariantCulture);
TotalOrdersPaid.Add(Created);
}
}
Eg TotalOrdersPaid should contain a list with number of orders by CreationDate.
What is a good way to achieve this?
Thanks
basically, you just need a group by and and ordering.
var result = orders//add any where clause needed
.GroupBy(m => m)
.Select(m => new {
cnt = m.Count(),
date = m.Key
})
.OrderByDescending(m => m.cnt);
Of course, you can add any DateTime.Parse / ParseExact in the Select, and / or project to a corresponding class.
To group the orders by date, take following LinQ lambda expression:
var grouped = orders.Where(o => o.PaymentStatus == PaymentStatus.Paid)
.GroupBy(g => g.CreatedOnUtc);
Now, all paid orders are grouped by date. To count the orders per date, select the key which is the date, and the Count() will count all orders for that date.
var counted = grouped.Select(s => new { Date = s.Key, Count = s.Count() });
Edit:
In one statement:
var result = orders.Where(o => o.PaymentStatus == PaymentStatus.Paid)
.GroupBy(g => g.CreatedOnUtc)
.Select(s => new { Date = s.Key, Count = s.Count() });
Based on your list of dates, the output will look like:
Date Count
5/04/2014 4
6/04/2014 3
Update:
If you want to put more properties in the anonymous type that will be returned from the Select() method, sumply just add them. If, for example, you want the date, the count and the list of orders for that date, use following line of code:
var result = orders.Where(o => o.PaymentStatus == PaymentStatus.Paid)
.GroupBy(g => g.CreatedOnUtc)
.Select(s => new
{
Date = s.Key,
Count = s.Count(),
Items = s.ToList()
});
Now you can do following:
foreach(var orderGroup in result)
{
Console.WriteLine(String.Format("Number of orders for {0}: {1}", orderGroup.Date, orderGroup.Count));
foreach(var order in orderGroup.Items)
{
Console.WriteLine(order.Name);
}
}
This will loop over all grouped items and display a sentence like:
Number of orders for 5/04/2014: 4
And then it will display the name of each order for that date (if your order has a property Name). Hope this clears everything out.