Linq For Each - Need Advice - c#

I have this block of code.
var foundAppointments = ServiceLink.FindAppointments(User, SiteSecureKey);
var MondayAppointments = foundAppointments.SelectMany(x => x.Where(y => y.StartTime.Date.DayOfWeek == DayOfWeek.Monday)).ToList();
Now I have a global list which I add each days appointments too e.g. Monday, Tuesday, Wed etc..
Then I run this:
Appointments.Add(MondayAppointments); ..........
Is there any whay I can run the Monday through Sunday appointments than having to write.
var TuesdayAppointments = foundAppointments.SelectMany(x=>x.Where(y => y.StartTime.Date.DayOfWeek == DayOfWeek.Tuesday)).ToList();
var WednesdayAppointments = foundAppointments.SelectMany(x=>x.Where(y => y.StartTime.Date.DayOfWeek == DayOfWeek.Wednesday)).ToList();
var ThursdayAppointments = foundAppointments.SelectMany(x=>x.Where(y =>y.StartTime.Date.DayOfWeek == DayOfWeek.Thursday)).ToList();
var FridayAppointments = foundAppointments.SelectMany(x=>x.Where(y => y.StartTime.Date.DayOfWeek == DayOfWeek.Friday)).ToList();
var SaturdayAppointments = foundAppointments.SelectMany(x => x.Where(y => y.StartTime.Date.DayOfWeek == DayOfWeek.Saturday)).ToList();
var SundayAppointments = foundAppointments.SelectMany(x => x.Where(y => y.StartTime.Date.DayOfWeek == DayOfWeek.Saturday)).ToList();
Appointments.Add(TuesdayAppointments);
Appointments.Add(WednesdayAppointments);
Appointments.Add(ThursdayAppointments);
Appointments.Add(FridayAppointments);
Appointments.Add(SaturdayAppointments);
Appointments.Add(SundayAppointments);
Any ideas?

You can enumerate over values from DayOfWeek enum and get appointments of each day:
var days = Enum.GetValues(typeof(DayOfWeek));
foreach (DayOfWeek dayOfWeek in days)
{
var dayAppointments = foundAppointments.SelectMany(x => x.Where(y => y.StartTime.Date.DayOfWeek == dayOfWeek)).ToList();
Appointments.Add(dayAppointments);
}
If you need Monday to be first, then sort days:
var days = Enum.GetValues(typeof(DayOfWeek)).Cast<DayOfWeek>()
.OrderBy(d => d == DayOfWeek.Sunday)
.ThenBy(d => d);
BTW why you are adding appointments day by day? Looks like you can add all at once. If you want to add appointments in order starting from Monday:
var appintmentsToAdd = foundAppointments
.SelectMany(x => x)
.OrderBy(a => a.StartTime.Date.DayOfWeek == DayOfWeek.Sunday)
.ThenBy(a => a.StartTime.Date.DayOfWeek)
.ToList();
Appointments.Add(appintmentsToAdd);

Use Enum.GetValues to get all possible DayOfWeek values:
var days = Enum.GetValues(typeof(DayOfWeek)).Cast<DayOfWeek>().ToArray();
And AddRange to add more than one element to Appointments at once:
var foundAppointments = ServiceLink.FindAppointments(User, SiteSecureKey);
Appointments.AddRange(days.Select(d => foundAppointments.SelectMany(x=>x.Where(y => y.StartTime.Date.DayOfWeek == d)).ToList());
Or just create Appointments directly from your query:
var Appointments.AddRange(days.Select(d => foundAppointments.SelectMany(x=>x.Where(y => y.StartTime.Date.DayOfWeek == d)).ToList()).ToList();

Try this:
IEnumerable<DayOfWeek> daysOfWeek = Enum.GetValues(typeof(DayOfWeek)).Cast<DayOfWeek>();
var appointmentsOnDaysOfWeek = daysOfWeek
.GroupJoin(
foundAppointments,
dayOfWeek => dayOfWeek,
appointment => appointment.StartDate.DayOfWeek,
(day, appointments) => new
{
Day = day,
Appointments = appointments.ToList()
})
.OrderBy(appointmentsOnDay => appointmentsOnDay.Day)
.Select(appointmentsOnDay => appointmentsOnDay.Appointments);
Appointments.AddRange(appointmentsOnDaysOfWeek);
Update:
I didn't pay attension to ordering by days when monday should be first. To fix it replace
.OrderBy(appointmentsOnDay => appointmentsOnDay.Day)
with
.OrderBy(appointmentsOnDay => appointmentsOnDay.Day == DayOfWeek.Sunday)
.ThenBy(appointmentsOnDay => appointmentsOnDay.Day)

Related

Count number of Mondays, Tuesdays etc from table using Linq

I'm trying to figure out how to count the number of Mondays, Tuesdays etc in a table using Linq and C#
Here is my sample data:
Status StatusDate
DELIVRD 2015-04-16 11:57:47.000
DELIVRD 2015-04-16 13:02:57.000
I know I need to use Group by to group the same Mondays, Tuesdays etc as 1.
My attempt:
var mondays = rad.SMSSentItems
.Where(x => (x.StatusDate.Value.DayOfWeek == DayOfWeek.Monday)
&& (x.Status == "DELIVRD"))
.ToList()
.Count;
You need to filter by the desired Status (DELIVRD) then group them by DayOfWeek of the status date
var weekDays = rad.SMSSentItems
.Where(x => x.Status == "DELIVRD")
.AsEnumerable()
.GroupBy(x => x.StatusDate.Value.DayOfWeek)
.Select(g => {
//Total items sent on this day of the week
var totalItemCount = g.Count();
//Total number if this distinct day of the week
var totalNumberOfDays = g.Select(x => x.StatusDate.Value.Date).Distinct().Count();
return new {
DayOfWeek = g.Key,
TotalItemCount = totalItemCount,
TotalNumberOfDays = totalNumberOfDays,
AverageItemPerDay = totalItemCount / totalNumberOfDays
};
})
.ToList();

Return results for closest Date if given Date does not exist in database using LINQ

I am trying to return a set of results based on a given date and if that date does not exist then then I want to return the result from the closet past date to that.
I am trying to return the results from an ApiController. The method I am using is pretty slow and I'm sure it's not the best one.
[HttpPost]
public IHttpActionResult GetItemsForDate(DateDTO Date)
{
using (var context = new CafeteriaContext())
{
bool vreauTOT = Date.vreauTOT;
var itemsList = new List<MenuItem>();
var getDates = context.MenuItems.Where(d => d.Date == Date.Date || d.Date < Date.Date).Select(d => d.Date).ToList();
var availableDate = getDates.OrderByDescending(t => t.Date).First();
if (vreauTOT)
{
itemsList = context.MenuItems
.Where(d => d.Date == availableDate)
.Select(r => r)
.ToList();
}
else
{
itemsList = context.MenuItems
.Where(d => d.Date == availableDate)
.Where(d => d.OnlyExternal == false)
.Select(r => r)
.ToList();
}
return Ok(itemsList);
}
Is it possible to save a trip to the database and maybe construct a single query that will return the same results ? Or maybe a faster way than what I am doing right now.
You probably don't need if .. else here. It can be reduced to below using compound condition
itemsList = context.MenuItems
.Where(d => d.Date == availableDate && (!vreauTOT && d => !d.OnlyExternal))
.ToList();
itemsList = context.MenuItems
.Where(d => d.Date == availableDate)
.Select(r => r)
.ToList();
No need to use Select here.
itemsList = context.MenuItems
.Where(d => d.Date == availableDate)
.Where(d => d.OnlyExternal == false)
.Select(r => r)
.ToList();
No need to use Select here.
Use 1 where and check the conditions there:
.Where(d => d.Date == availableDate && ! d.OnlyExternal)
Explanation: each LINQ-method will perform a loop in the background, and the more loops you create, the slower it will run.

.SelectMany with C#

I found this post and I need something similar
stackoverflow.com/questions/41282053/groupby-multiple-date-properties-by-month-and-year-in-linq/43112774#43112774
I need to group by month and year, but at the same time I need a property of the main element, which I can not access
var departments = stops
.SelectMany(x => new[] { x.InitDate.Month, x.InitDate.Year }
.Where(dt => dt != null).Select(dt => x.InitDate))
.GroupBy(dt => new { dt.Month, dt.Year })
.OrderBy(g => g.Key.Month)
.ThenBy(g => g.Key.Year)
.Select(g => new
{
Key = g.Key.Month,
Año = g.Key.Year,
Duration = 0,
Count = g.Count()
});
I would need access to "stops.Duration" but if i do this: .SelectMany(x => new[] { x.InitDate.Month, x.InitDate.Year, x.Duration }
it does not group me by month-year
Can anybody help me?
Sorry for my english and thank you very much
This code should do:
var departments = stops
.Where(stop => stop.InitDate != null)
.SelectMany(stop => new[] { Month = stop.InitDate.Month, Year = stop.InitDate.Year, Duration = stop.Duration })
.GroupBy(dt => new { dt.Month, dt.Year })
.OrderBy(g => g.Key.Month)
.ThenBy(g => g.Key.Year)
.Select(g => new
{
Key = g.Key.Month,
Año = g.Key.Year,
Duration = g.Sum(v => v.Duration),
Count = g.Count()
});
It selects the duration, groups on month and year, and uses the sum of the duration from the grouped result.
int Month = 0, Year= 0, Duration = 0;
var departments = stops
.Where(stop => stop.InitDate != null)
.SelectMany(stop => new[] { Month = stop.InitDate.Month, Year = stop.InitDate.Year, Duration = stop.Duration })
.GroupBy(dt => new { Month, Year })
.OrderBy(g => g.Key.Month)
.ThenBy(g => g.Key.Year)
.Select(g => new
{
Key = g.Key.Month,
Año = g.Key.Year,
Duration = g.Sum(v => Duration),
Count = g.Count()
});
For me, this is the final solution

Grouping earliest entry for each day using Linq

Trying to get my head around Linq, and at the same time keep track of the time I log on in the morning, which should be the time I get into the office thereabouts.
My code so far is:
EventLog SecurityLog = new EventLog("Security");
var AccountLoggedOnEntries = SecurityLog.Entries.Cast<EventLogEntry>()
.Where(x => x.InstanceId == 4624)
.Select(x => new
{
DateGenerated = x.TimeGenerated.ToShortDateString()
,
TimeGenerated = x.TimeGenerated.ToShortTimeString()
,
x.Message
})
.ToList();
DgvLogSummary.DataSource = AccountLoggedOnEntries;
DgvLogSummary.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells;
I want to filter the results so that I only have one entry for each day, which is the earliest time.
In SQL I would normally take the Message of the earliest entry and then group by all fields.
How do I perform a similar query in Linq?
In LINQ you would group by, sort each group, and pick the first item:
var AccountLoggedOnEntries = log.Entries.Cast<EventLogEntry>()
.Where(x => x.InstanceId == 4624)
.GroupBy(x => x.TimeGenerated.Date)
.Select(g => g.OrderBy(x => x.TimeGenerated).First())
.Select(x => new {
DateGenerated = x.TimeGenerated.ToShortDateString()
, TimeGenerated = x.TimeGenerated.ToShortTimeString()
, x.Message
})
.ToList();
You could GroupBy the date and then select the minimum time
var AccountLoggedOnEntries = log.Entries.Cast<EventLogEntry>()
.Where(x => x.InstanceId == 4624)
.GroupBy(x => x.TimeGenerated.Date)
.Select(x => new {
DateGenerated = x.Key
, TimeGenerated = x.Min(y => y.TimeGenerated).ToShortTimeString()
})
.ToList();
Getting the appropriate Message is a little more tricky. One easy option is to use x.First().Message in the above Select projection.
Try this :
var AccountLoggedOnEntries = log.Entries.Cast<EventLogEntry>()
.Where(x => x.InstanceId == 4624)
.GroupBy(x => x.TimeGenerated.Date)
.Select(days => days.OrderBy(time => time.TimeGenerated).FirstOrDefault())
.Select(x => new
{
DateGenerated = x.TimeGenerated.ToShortDateString()
,
TimeGenerated = x.TimeGenerated.ToShortTimeString()
,
x.Message
})
.ToList();

Linq average count by day of week

I'm trying to get the total average count of row instances by day of week. So over the past year, I'm trying to get the average amount of rides that happened on a monday/tuesday/wed/ect.
Here's what I have so far. That gives me the total count per day of week, but not the average count.
UnitOfWork.Query<WorkoutRecord>()
.Where(x => x.WorkoutDate > baseDate)
.GroupBy(x => SqlFunctions.DatePart("weekday", x.UploadDate))
.Select(x => new AdminDashboardWorkoutsGroupedDay()
{
DayOfWeek = (DayOfWeek)x.Key,
WorkoutCount = x.Count()
}).ToList();
If I understand you well, in the end you're trying to get one number, the average count per weekday. This requires a second grouping that reduces the data to one group:
UnitOfWork.Query<WorkoutRecord>()
.Where(x => x.WorkoutDate > baseDate)
.GroupBy(x => SqlFunctions.DatePart("weekday", x.UploadDate))
.Select(x => new AdminDashboardWorkoutsGroupedDay()
{
DayOfWeek = (DayOfWeek)x.Key,
WorkoutCount = x.Count()
})
.GroupBy(g => 0) // or g => "x", or whatever
.Select (g1 => (decimal)g1.Sum(x => x.WorkoutCount) / g1.Count())
.ToList();
Here's what worked. Thanks #Gert Arnold, you got me really close. I had to group by day and count all the workouts, then take the average of that grouping by weekday.
UnitOfWork.Query<WorkoutRecord>()
.Where(x => x.WorkoutDate > baseDate && x.TotalTicks > 600)
.GroupBy(x => EntityFunctions.TruncateTime(x.UploadDate))
.Select(x => new
{
Date = x.Key ?? DateTime.UtcNow,
TheCount = x.Count()
})
.GroupBy(x=> SqlFunctions.DatePart("weekday", x.Date))
.Select (x => new AdminDashboardWorkoutsGroupedDay()
{
WorkoutCount = (x.Average(y=>y.TheCount)),
DayOfWeek = (DayOfWeek)x.Key
})
.ToList()
It's not clear what property you want the average of, but assuming you have a property like WorkoutRecord.Duration, you should be able to get the average like:
UnitOfWork.Query<WorkoutRecord>()
.Where(x => x.WorkoutDate > baseDate)
.GroupBy(x => SqlFunctions.DatePart("weekday", x.UploadDate))
.Select(x => new AdminDashboardWorkoutsGroupedDay()
{
DayOfWeek = (DayOfWeek)x.Key,
AverageDuration = x.Average(w => w.Duration)
})
.ToList();

Categories

Resources