How to add empty groups to a Linq query result set? - c#

I have a very simple query which selects items from a table based on matching the month and then grouping by day. These groups are then used as a data source for a repeater which outputs the group elements as entries "per day".
The problem is that days that don't exist (i.e. there's no group for) will naturally not be displayed, so things for the 6th and 8th, when there's nothing for the 7th, will be seen directly next to each other (think of a calendar view). The question is, given my query below, how could I insert groups with no elements, even when there's no entry for that day?
IQueryable events =
Events
.Where(i => i.Date.Month == date.Month)
.GroupBy(i => i.Date.Day);
I can do this figuring out after the fact, but can I account for it to get the result set at once? Or can a previous tried & tested approach be recommended?

Create your result set like this:
var date = ...;
var events = Enumerable.Range(1, DateTime.DaysInMonth(date.Year, date.Month))
.ToDictionary(
day => new DateTime(date.Year, date.Month, day),
day => new List<Event>());
Then insert into it like this:
var query = Events
.Where(e => e.Date.Year == date.Year)
.Where(e => e.Date.Month == date.Month);
foreach (var e in query)
events[e.Date].Add(e);
If you really want to do this server-side as part of the query, you'll either need to (A) send the list of dates you're interested in as part of the query, or (B) use DBMS-specific functions to construct the set of days you're interested in server-side.
Specific to (B) and SQL, there are things like this: Get the first and last date of next month in MySQL
I personally would just do this client-side. The database query gets whatever data your implementation needs to extract from the database; the user gets whatever information your view needs. To query the database for the data you need to create your view, you don't actually need to know the list of dates in your month, so it really shouldn't be part of the query.

Related

How is it possible that selecting first after GroupBy on a DataTable does not return one value for every group?

I am trying to get one row per id from a DataTable, and I do not care which row I take. The same id can exist on several rows in the table.
Here's the expression that's giving me trouble:
dt.AsEnumerable().GroupBy(i => i.Field<int>("id")).Select(i => i.First())
Running just this section dt.AsEnumerable().GroupBy(i => i.Field<int>("id") correctly gives me a result of 22 groupings for my DataTable. (I have 22 ids with data in this table)
However, when adding on the .Select(i => i.First()), I am only seeing 10 data rows.
To me this doesn't seem to make any sense. If the GroupBy function managed to find 22 distinct id values, I would expect this logic to grab one of each.
My only other thought is that maybe it's just a weird side effect of viewing this data through a watch in Visual Studio rather than assigning to a variable.
If you think it's just weird side effects of viewing the data in a watch, which can happen with LINQ statements, then split it out into
var groups = dt.AsEnumerable().GroupBy(i => i.Field<int>("id")).ToList();
var firstOfGroups = groups.Select(i => i.First()).ToList();
and then look at groups and firstOfGroups in the debugger. Temporarily evaluating items with .ToList() can help a lot with viewing things in the debugger.
I think it is possible, can double check the count of each group items
.Select(g=>new { k = g.Key, c = g.Count() })

Using linq to retrieve distinct dates

Hi I'm having trouble retrieving distinct dates from a database. In this database I have several events on any particular day and results show a list of events with the same date / 'StartDate' field. How could I retrieve just the distinct days. I've tried:
ICollection result;
result= client.GetEventInstances().Select(x => x.StartDate).Distinct();
I expect to see just one distinct date along with just the first event for that date only.
Then you need to group on the events and get the first item out of every date:
client.GetEventInstances()
.GroupBy(k => k.StartDate.Date)
.ToDictionary(k => k.Key, v => v.First())
Well, I managed to search and find a solution for my needs, it wont suit everyone of course. Thanks for the time people gave me on this, always appreciated
ICollection<EventInstance> result;
result = client.GetEventInstances();
IEnumerable<EventInstance> distinctDate = result
.GroupBy(e => e.StartDate.Date)
.Select(group => group.First());

Get "lowest" date of parents great grand child with Linq

I'm trying to learn LINQ - the hard way.
I have a few entities which all are connected in sequence
Department -> Groups -> Works -> project
project has a startdate (and an end date)
I'm trying to get the startdate of the Group with the first startdate so to speak.
I've tried:
Department.Groups.Select(g => g.Works.Select(w => w.project.StartDate).Min())
and various variations thereof.
The problem being it returns a list in a list in a list, and I'm getting dizzy from just thinking about it :)
I've tried to work my way backwards from
g.Works.Select(w=> w.Project.StartDate).Min() which gives me the lowest date for Works
Any help is greatly appreciated
Flatten your List with SelectMany and then apply Min like:
var minDate = Department.Groups
.SelectMany(g => g.Works.Select(w => w.project.StartDate))
.Min();
That will return the minimum date for all the Works under all Groups.
If you just want the date then Habib's flatten solution is good. If you want the whole record you can order by and then take the first record, like this:
Department
.Groups
.OrderBy(g => g.Works.Select(w => w.project.StartDate))
.FirstOrDefault()

Compare DateTime in a generic list

I'm trying to query a generic list using code below.
ArticleStartDate = Convert.ToDateTime("2013-05-07 09:00:00.450");
var queryable = context.GetQueryable<FeaturedArticleResultItem>()
queryable = queryable.Where(t => t.StartDisplay > ArticleStartDate).OrderBy(t => t.StartDisplay);
I'd like to get results which has a 'StartDisplay' date time later then 9:00am 7th of May, and my query returns list of items later then the day (7th, which is good), however, I've also got the items in the list like 8:30am 7th of May. It looks like it compares the date ok, but not the time...
Hope the above make sense.
Just a bit more info, I just found that t.StartDisplay looks like this {7/05/2013 1:31:00 a.m.}. Does it matter when comparing the time?

Select All from Collection Where ID in List

I'm just learning to use lambda expressions and was turned on to Dapper a couple days ago. I'm working on an app that populates a listView with usernames to be selected for some account maintenance. I'm having trouble with one part of sorting:
I'm getting a collection of objects using a Dapper query and assigning it to the variable Global.allUsers.
I have a list of IDs (List<int> migrated) that have already been used, and so don't need to appear in the listView
I'm getting a list of all my users using the following:
var uniqUsers = Global.allUsers.OrderBy(n => n.lastNames).GroupBy(q => q.salesIDs);
To populate the listView with only the users who haven't been migrated, I need to select only those users without q.salesIDs in migrated. I just can't quite figure out if/how I can do this without using some kind of foreach. Seems like there should be a way to select it.
Granted, I'm looping through uniqUsers anyway, and populating the listView with values out of each object's properties. I can add a statement to check if the current ID is in migrated, but my gut is just telling me that I can do it with the select statement and save myself a step.
I'm thinking something like this:
var uniqUsers = Global.allUsers.Where(i => i.salesIDs not in migrated).OrderBy(n => n.lastNames).GroupBy(q => q.salesIDs);
but i => i.salesIDs not in migrated isn't cutting it.
This does what you need:
var uniqUsers = allUsers.Where(x => migrated.Contains(x.salesIDs))
.OrderBy(y => y.lastNames)
.GroupBy(z => z.salesIDs).SelectMany(v => v).ToList();
Gets all users where the salesID is in migrated, then orders by lastNames and then groups by the salesID. The SelectMany() will project the elements to a sequence, the ToList() will enumerate the results.

Categories