I am struggling with a grouping linq query
I have this:
DateTime dateFrom = new Date(2014,8,2);
var GroupedPrices = Prices.Where(p => p.ArrivalDateFrom <= dateFrom
&&
p.ArrivalDateTo > dateFrom)
.GroupBy(p => p.ItemID);
I am trying to get to a single price of each ID - eg. in/from each group, based on the newest ValidFrom date of that price.
I have started by grouping them by their ItemID's (not the individual price record id's) but am struggling in working out how to grab just a single one based on that newest ValidFrom date. I thought I could order them before grouping, but wasn't sure that would stick after the grouping was done.. ie
expecting it will use a Max(x=>x.ValidFrom) type thing or OrderByDescending(x=>x.ValidFrom).First() but cant work that out
any help much appreciated
thanks
I think you just need to select what you want, at the end, like so:
DateTime dateFrom = new Date(2014,8,2);
var GroupedPrices = Prices
.Where(p => p.ArrivalDateFrom <= dateFrom && p.ArrivalDateTo > dateFrom)
.GroupBy(p => p.ItemID)
.Select(g => new{ ItemId = g.Key, NewestPrice = g.OrderByDescending(p => p.ValidFrom).First() });
Related
I'm using ASP.NET MVC, C# and SQL Server.
I want to get a list of cars that are available for rent within a certain date, ie. not already rented out.
What I got now is something like taking all the cars from the cars table, except the ones that have an order at the specified date (instead of checking each car individually for an order):
var query = db.Cars.Select(r => r.ID)
.Except(db.Orders
.Where(o => (startDate >= o.RentalStart
&& startDate <= o.RentalReturn))
Select(r => r.Car.ID));
The problem is that I want to return a custom object, and I'm not sure how do that using except, so I could always get the ID, and based on the ID get the entire object with a different method/query.
But I'd like to get something like:
Select(c => new myModel
{
ID = c.ID,
property1 = c.property1,
property2 = c.property2,
property3 = c.property3,
property4 = c.property4,
});
What you can do is filter the Cars list where the ID is not contained in the set of invalid order IDs.
var invalidOrders = db.Orders
.Where(o => startDate >= o.RentalStart && startDate <= o.RentalReturn)
.Select(r => r.Car.ID)
var query = db.Cars.Where(car => !invalidOrders.Contains(car.ID));
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.
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.
I am currently working through a problem where I would like to run a query which groups the results by the date selected.
For this example, imagine a simple model like so:
public class User
{
public DateTime LastLogIn {get; set;}
public string Name {get; set;}
}
The solution I am looking for is to get a count of Users logged in by Date.
In the database the DateTime are stored with both date and time components, but for this query I really only care about the date.
What I currently have is this:
context.Users
.Where((x.LastLogIn >= lastWeek)
&& (x.LastLogIn <= DateTime.Now))
.GroupBy(x => EntityFunctions.TruncateTime(x.LastLogIn))
.Select(x => new
{
Value = x.Count(),
Day = (DateTime)EntityFunctions.TruncateTime(x.Key)
}).ToList();
The above however returns an empty list.
End goal is to have a List of objects, which contain a Value (the count of users logged in on a day) and a Day (the day in question)
Any thoughts?
Upon changing the query to:
context.Users
.Where((x.LastLogIn >= lastWeek)
&& (x.LastLogIn <= DateTime.Now))
.GroupBy(x => EntityFunctions.TruncateTime(x.LastLogIn))
.Select(x => new
{
Value = x.Count(),
Day = (DateTime)x.Key
}).ToList();
it now returns a list with a single item, with the Value being the total count of Users that match the where clause, and the Day being the very first day. It still hasn't seemed to be able to group by the days
NOTE: turns out the above code is right, I was just doing something else wrong.
Sql that it is generating is (note might be very slight syntactical errors here with me adjusting it for the example):
SELECT
1 AS [C1],
[GroupBy1].[A1] AS [C2],
CAST( [GroupBy1].[K1] AS datetime2) AS [C3]
FROM ( SELECT
[Filter1].[K1] AS [K1],
COUNT([Filter1].[A1]) AS [A1]
FROM ( SELECT
convert (datetime2, convert(varchar(255), [Extent1].[LastLogIn], 102) , 102) AS [K1],
1 AS [A1]
FROM [dbo].[Users] AS [Extent1]
WHERE (([Extent1].[LastLogIn] >= #p__linq__1) AND ([Extent1].[LastLogIn] <= #p__linq__2)
) AS [Filter1]
GROUP BY [K1]
) AS [GroupBy1]
You do not need the second TruncateTime in there:
context.Users
.Where((x.LastLogIn >= lastWeek) && (x.LastLogIn <= DateTime.Now))
.GroupBy(x => DbFunctions.TruncateTime(x.LastLogIn))
.Select(x => new
{
Value = x.Count(),
// Replace the commented line
//Day = (DateTime)DbFunctions.TruncateTime(x.Key)
// ...with this line
Day = (DateTime)x.Key
}).ToList();
The GroupBy has truncated the time from the DateTime already, so you do not need to call it again.
To use DbFunctions.TruncateTime you'll need to reference the assembly System.Data.Entity and include using System.Data.Entity;
Note: Edited to address deprecation of EntityFunctions.
Try this:
.GroupBy(x => new {Year = x.LastLogIn.Year, Month = x.LastLogIn.Month, Day = x.LastLogIn.Day)
.Select(x => new
{
Value = x.Count(),
Year = x.Key.Year,
Month = x.Key.Month,
Day = x.Key.Day
})
You can do it easily:
yourDateList.GroupBy(i => i.ToString("yyyyMMdd"))
.Select(i => new
{
Date = DateTime.ParseExact(i.Key, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None),
Count = i.Count()
});
I came across this same problem to get a count based on group by a datetime column. This is what i did. Thanks for some of the answers here. I tried the minimal version and I used this code in .netcore solution.
var result = _context.yourdbmodel
.GroupBy(x=>x.yourdatetimecolumn.Value.Date)
.select(x=> new
{
Count = x.Count(),
Date = (DateTime)x.Key // or x.Key.Date (excluding time info) or x.Key.Date.ToString() (give only Date in string format)
})
.ToList();
Sample output:
[ {
"requestDate": "2020-04-01",
"requestCount": 3 }, {
"requestDate": "2020-04-07",
"requestCount": 14 } ]
Hope this helps someone in future.
You can also do it in one line.
var b = (from a in ctx.Candidates select a)
.GroupBy(x => x.CreatedTimestamp.Date).Select(g => new { Date=(g.Key).ToShortDateString(), Count = g.Count() });
Just convert first IQueryable to IEnumerable(List) with the help of ToList() then use groupby
context.Users
.Where((x.LastLogIn >= lastWeek) && (x.LastLogIn <= DateTime.Now))
.ToList()
.GroupBy(x => x.LastLogIn.Date))
.Select(x => new
{
Value = x.Count(),
Day = (DateTime)x.Key
}).ToList();
The LINQ query below is working fine but I need to tweak it a bit.
I want all the records in the file grouped by recordId (a customer number) and then ordered by, in descending order, the date. I'm getting the grouping and the dates are in descending order. Now, here comes the tweaking.
I want the groups to be sorted, in ascending order, by recordId. Currently, the groups are sorted by the date, or so it seems. I tried adding a .OrderBy after the .GroupBy and couldn't get that to work at all.
Last, I want to .take(x) records where x is dependent on some other factors. Basically, the .take(x) will return the most-recent x records. I tried placing a .take(x) in various places and I wasn't getting the correct results.
var recipients = File.ReadAllLines(path)
.Select (record => record.Split('|'))
.Select (tokens => new
{
FirstName = tokens[2],
LastName = tokens[4],
recordId = tokens[13],
date = Convert.ToDateTime(tokens[17])
}
)
.OrderByDescending (m => m.date)
.GroupBy (m => m.recordId)
.Dump();
Edit #1 -
recordId is not unique. There may / will likely be multiple records with the same recordId. recordId is actually a customer number.
The output will be a resultset with first name, last name, date, and recordId. Depending on several factors, there many be 1 to 5 records returned for each recordId.
Edit #2 -
The .Take(x) is for the recordId. Each recordId may have multiple rows. For now, let's assume I want the most recent date for each recordId. (select top(1) when sorted by date descending)
Edit #3 -
The following query generates the following results. Note each recordId only produces 1 row in the output (this is okay) and it appears it is the most recent date. I haven't thouroughly checked this yet.
Now, how do I sort, in ascending order, by recordId?
var recipients = File.ReadAllLines(path)
.Select (record => record.Split('|'))
.Select (tokens => new
{
FirstName = tokens[2],
LastName = tokens[4],
recordId = Convert.ToInt32(tokens[13]),
date = Convert.ToDateTime(tokens[17])
}
)
.GroupBy (m => m.recordId)
.OrderByDescending (m => m.Max (x => x.date ) )
.Select (m => m.First () )
.Dump();
FirstName LastName recordId date
X X 2531334 3/11/2011 12:00:00 AM
X X 1443809 10/18/2001 12:00:00 AM
X X 2570897 3/10/2011 12:00:00 AM
X X 1960526 3/10/2011 12:00:00 AM
X X 2475293 3/10/2011 12:00:00 AM
X X 2601783 3/10/2011 12:00:00 AM
X X 2581844 3/6/2011 12:00:00 AM
X X 1773430 3/3/2011 12:00:00 AM
X X 1723271 2/4/2003 12:00:00 AM
X X 1341886 2/28/2011 12:00:00 AM
X X 1427818 11/15/1986 12:00:00 AM
You can't that easily order by a field which is not part of the group by fields. You get a list for each group. This means, you get a list of date for each recordId.
You could order by Max(date) or Min(date).
Or you could group by recordId and date, and order by date.
order by most recent date:
.GroupBy (m => m.recordId)
// take the most recent date in the group
.OrderByDescending (m => m.Max(x => x.date))
.SelectMany(x => x.First
The Take part is another question. You could just add Take(x) to the expression, then you get this number of groups.
Edit:
For a kind of select top(1):
.GroupBy (m => m.recordId)
// take the most recent date in the group
.OrderByDescending (m => m.Max(x => x.date))
// take the first of each group, which is the most recent
.Select(x => x.First())
// you got the most recent record of each recordId
// and you can take a certain number of it.
.Take(x);
snipped I had before in my answer, you won't need it according to your question as it is now:
// create a separate group for each unique date and recordId
.GroupBy (m => m.date, m => m.recordId)
.OrderByDescending (m => m.Key)
This seems very similar to your other question - Reading a delimted file using LINQ
I don't believe you want to use Group here at all - I believe instead that you want to use OrderBy and ThenBy - something like:
var recipients = File.ReadAllLines(path)
.Select (record => record.Split('|'))
.Select (tokens => new
{
FirstName = tokens[2],
LastName = tokens[4],
recordId = tokens[13],
date = Convert.ToDateTime(tokens[17])
}
)
.OrderBy (m => m.recordId)
.ThenByDescending (m => m.date)
.Dump();
For a simple Take... you can just add this .Take(N) just before the Dump()
However, I'm not sure this is what you are looking for? Can you clarify your question?
just add
.OrderBy( g=> g.Key);
after your grouping. This will order your groupings by RecordId ascending.
Last, I want to .take(x) records where
x is dependent on some other factors.
Basically, the .take(x) will return
the most-recent x records.
If you mean by "the most recent" by date, why would you want to group by RecordId in the first place - just order by date descending:
..
.OrderByDescending (m => m.date)
.Take(x)
.Dump();
If you just want to get the top x records in the order established by the grouping though you could do the following:
...
.GroupBy (m => m.recordId)
.SelectMany(s => s)
.Take(x)
.Dump();
If you want something like the first 3 for each group, then I think you need to use a nested query like:
var recipients = File.ReadAllLines(path)
.Select(record => record.Split('|'))
.Select(tokens => new
{
FirstName = tokens[2],
LastName = tokens[4],
RecordId = tokens[13],
Date = Convert.ToDateTime(tokens[17])
}
)
.GroupBy(m => m.RecordId)
.Select(grouped => new
{
Id = grouped.Key,
First3 = grouped.OrderByDescending(x => x.Date).Take(3)
}
.Dump();
and if you want this flattened into a record list then you can use SelectMany:
var recipients = var recipients = File.ReadAllLines(path)
.Select(record => record.Split('|'))
.Select(tokens => new
{
FirstName = tokens[2],
LastName = tokens[4],
RecordId = tokens[13],
Date = Convert.ToDateTime(tokens[17])
}
)
.GroupBy(m => m.RecordId)
.Select(grouped => grouped.OrderByDescending(x => x.Date).Take(3))
.SelectMany(item => item)
.Dump();