This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
Earlier I had posted the question Sorting Date and Time.
It was answered by mipe34, but I am stuck trying to use the solution inside my code.
Here's that I did so far. I created a class as follows:
public class Meals
{
public DateTime Start { get; set; }
public DateTime End { get; set; }
}
In my main class I have the following method that has date pair values:
IList<DateTime> starts = new List<DateTime>();
IList<DateTime> ends = new List<DateTime>();
DateTime breakStart1 = new DateTime(2012, 02, 15, 12, 30, 00); // 15/02/12 12.30PM
DateTime breakEnd1 = new DateTime(2012, 02, 15, 13, 30, 00); // 15/02/12 01.30PM
DateTime breakStart2 = new DateTime(2012, 02, 15, 11, 00, 00); // 15/02/12 11.00AM
DateTime breakEnd2 = new DateTime(2012, 02, 15, 12, 00, 00); // 15/02/12 12.00PM
DateTime breakStart3 = new DateTime(2012, 02, 15, 12, 00, 00); // 15/02/12 12.00PM
DateTime breakEnd3 = new DateTime(2012, 02, 15, 01, 00, 00); // 15/02/12 01.00PM
starts.Add(breakStart1);
starts.Add(breakStart2);
starts.Add(breakStart3);
ends.Add(breakEnd1);
ends.Add(breakEnd2);
ends.Add(breakEnd3);
for (int i = 0; i < starts.Count; i++)
{
var breaks = new List<Break>()
{
//for (int j= 0; j<starts.Count; j++)
//{
new Break()
{
MealStart = starts[i],
MealEnd = ends[i]
}
// }
};
var ordered = breaks.OrderBy(s => s.MealStart);
foreach (var ord in ordered)
{
System.Console.WriteLine(ord.MealStart);
System.Console.WriteLine(ord.MealEnd);
}
}
How can I pass all my date pairs and use his solution? it doesn't bring back the expected value.
Start by creating objects and adding them to a list. You can use the collection initializer in this case.
var mealList = new List<Meals>
{
new Meals { Start = dtmealStart1, End = dtmealStart1 },
/* Repeat for the rest of them possibly incorporating some kind of loop to generate this list */
};
//and then get your sorted list
var sortedMealList = mealList.OrderBy(m => m.Start);
Related
I have a list of DateTime values with dates that contain hours and minutes:
List<DateTime> times = times = new List<DateTime>()
{
new DateTime(2019, 01, 01, 17, 00, 00),
new DateTime(2019, 01, 01, 18, 45, 00),
new DateTime(2019, 01, 01, 19, 00, 00),
new DateTime(2019, 01, 01, 19, 30, 00),
new DateTime(2019, 01, 01, 22, 30, 00)
};
DateTime current = DateTime.Now;
I put them all in a ComboBox, and I want to make some sort of algorithm so when I load my form, it will check for the current time and find the closest value to the current time and select the ComboBox item that contains that hour.
How can I achieve this? I tried to loop through them all and check for the least hour, but that doesn't seem to work. Is there a smarter way to do it?
For example: If the current time is 17:32, it will choose 17:00, because that's the closest. But, if the current time is 18:20, it will choose 18:45 and so on.
Compare to the Ticks property of DateTime (MSDN). It can be seen as a linear representation of the whole date and timestamp and is sortable.
Do something like
comboBox.SelectedItem = times.OrderBy(t => Math.Abs(t.Ticks - current.Ticks)).First()
You could take the difference with DateTime.Now for all your datetimes, order by this difference and take the first result.
times.OrderBy(m => Math.Abs((DateTime.Now - m).TotalMilliseconds)).First();
You would have to select an instance of DateTime which minimizes the temporal distance to the current time. You could use an extension method for IEnumerable<T> to do that as follows.
public static T ArgMin<T, R>(T t1, T t2, Func<T, R> f)
where R : IComparable<R>
{
return f(t1).CompareTo(f(t2)) > 0 ? t2 : t1;
}
public static T ArgMin<T, R>(this IEnumerable<T> Sequence, Func<T, R> f)
where R : IComparable<R>
{
return Sequence.Aggregate((t1, t2) => ArgMin<T, R>(t1, t2, f));
}
var iNow = DateTime.Now;
var iResult = times.ArgMin(iTime => Math.Abs((iTime - iNow).Ticks));
Although very generic, this implementation does not involve any sorting.
You are looking for ArgMax which is not implemented in standard Linq, but can be emulated via Aggreagte
List<DateTime> times = new List<DateTime>() {
new DateTime(2019, 01, 01, 17, 00, 00),
new DateTime(2019, 01, 01, 18, 45, 00),
new DateTime(2019, 01, 01, 19, 00, 00),
new DateTime(2019, 01, 01, 19, 30, 00),
new DateTime(2019, 01, 01, 22, 30, 00),
};
DateTime toFind = new DateTime(2019, 5, 8, 18, 20, 0);
var closestTime = times
.Aggregate((best, item) => Math.Abs((item.TimeOfDay - toFind.TimeOfDay).Ticks) <
Math.Abs((best.TimeOfDay - toFind.TimeOfDay).Ticks)
? item
: best);
Please, note, that if we should find the closest time, we have to get rid of date part - TimeOfDay. If date part should be count, just remove TimeOfDay -
var closestDateAndTime = times
.Aggregate((best, item) => Math.Abs((item - toFind).Ticks) <
Math.Abs((best - toFind).Ticks)
? item
: best);
One option is to use MoreLinq's MinBy:
var actualNow = DateTime.Now;
// set `current` up however you need it
var current = new DateTime(2019, 01, 01, actualNow.Hour, actualNow.Minute, actualNow.Minute, actualNow.Millisecond); // set this up however you need it
var min = times.MinBy(z => Math.Abs((current - z).Ticks)).First();
It avoids the memory pressure of the OrderBy based solutions (since it avoids allocating a list to store the entire set of times).
Note you may want to check whether times is empty before using this (or other) solutions. If you don't wish to do that, consider using:
var min = times.MinBy(z => Math.Abs((current - z).Ticks)).Cast<DateTime?>().FirstOrDefault();
which will return null (rather than default(DateTime), or throw an exception) if times is empty.
I’m looking for a way to find holes in a schedule, times that does not have a booking.
I have a simple class in C# that looks like:
DateTime StartTime { get; set; }
Datetime EndTime { get; set; }
public int User_ID { get; set; }
The same class is used for the bookings aswell.
Let’s assume I have these objects:
Schedule: StartTime "2017-03-14 08:00" - EndTime "2017-03-14 16:00" (8 hours)
Booking: StartTime "2017-03-14 09:00" - Endtime "2017-03-14 10:00" (1 hour)
My final result from this would be 2 objects that represents the “free time”:
Free: StartTime "2017-03-14 08:00" EndTime: "2017-03-14 09:00" (1 hour)
Free: StartTime "2017-03-14 10:00" EndTime: "2017-03-14 16:00"(6 hour)
How would I check this in C#?
I'm thinking about looping the Schedule and split them on start/end of each booking, but I'm not sure how to do it.
It is easier than I thought... Note that this code isn't optimized, and this algorithm isn't probably very optimizable:
public class TimeSegment
{
public readonly DateTime StartTime;
public readonly DateTime EndTime;
public TimeSegment(DateTime startTime, DateTime endTime)
{
StartTime = startTime;
EndTime = endTime;
}
public TimeSegment[] Subtract(TimeSegment other)
{
// 8-10 Subtract 10-11 = 8-10
if (StartTime > other.EndTime || other.StartTime > EndTime)
{
// If there is no intersection, we return { this }
// (no subtraction)
return new[] { this };
}
if (StartTime >= other.StartTime)
{
// 8-10 Subtract 8-10 = (nothing)
// 8-10 Subtract 7-11 = (nothing)
if (EndTime <= other.EndTime)
{
// Total subtraction, nothing remains!
return new TimeSegment[0];
}
else
{
// 8-10 Subtract 7-9 = 9-10
return new[] { new TimeSegment(other.EndTime, EndTime) };
}
}
// 8-12 Subtract 9-13 = 8-9
if (EndTime <= other.EndTime)
{
return new[] { new TimeSegment(StartTime, other.EndTime) };
}
// 8-12 Subtract 9-11 = 8-9, 11-12
// Complete case: two TimeSegments returned
return new[] { new TimeSegment(StartTime, other.StartTime), new TimeSegment(other.EndTime, EndTime) };
}
public override string ToString()
{
return string.Format("{0}-{1}", StartTime, EndTime);
}
}
And then:
var schedules = new List<TimeSegment> { new TimeSegment(new DateTime(2017, 03, 14, 08, 00, 00), new DateTime(2017, 03, 14, 16, 00, 00)) };
var bookings = new List<TimeSegment>
{
new TimeSegment(new DateTime(2017, 03, 14, 09, 00, 00), new DateTime(2017, 03, 14, 10, 00, 00)),
new TimeSegment(new DateTime(2017, 03, 14, 12, 00, 00), new DateTime(2017, 03, 14, 14, 00, 00)),
new TimeSegment(new DateTime(2017, 03, 14, 13, 00, 00), new DateTime(2017, 03, 14, 15, 00, 00)),
};
foreach (TimeSegment booking in bookings)
{
var schedulesNew = new List<TimeSegment>();
foreach (TimeSegment schedule in schedules)
{
var diff = schedule.Subtract(booking);
schedulesNew.AddRange(diff);
}
schedules = schedulesNew;
}
There "core" of this is a Subtract function that given a TimeSegment subtracts from this another TimeSegment, returning 0, 1 or 2 TimeSegments... Then iteratively we subtract all the bookings from the TimeSegments that we produced from the previous booking.
I'm trying to seed a database with two lists. The first list is just a bunch of items. The second list is a bunch of junk that references the first list. I'm trying to reference items from first list in the second list of junk, via LINQ, but I'm fairly certain I'm not doing it right:
For example, List 1:
var items = new List<Item>()
{
new Item { ItemtId = 1, DateTime = new DateTime(2014, 09, 05, 12, 30, 30), Text = "Cheese" },
new Item { ItemtId = 1, DateTime = new DateTime(2014, 09, 05, 12, 30, 30), Text = "Lettuce" },
new Item { ItemtId = 1, DateTime = new DateTime(2014, 09, 05, 12, 30, 30), Text = "Ground Beef" },
new Item { ItemtId = 1, DateTime = new DateTime(2014, 09, 03, 12, 30, 30), Text = "Ketchup" },
new Item { ItemtId = 1, DateTime = new DateTime(2014, 09, 03, 12, 30, 30), Text = "Mustard" },
};
var junk= new List<Junk>()
{
new Junk { JunkId = 1, DateTime = new DateTime(2014, 09, 05, 10, 00, 00), Items = items.Where(d => d.DateTime.ToShortDateString() == new DateTime(2014, 09, 05).ToShortDateString()},
new Junk { JunkId = 2, DateTime = new DateTime(2014, 09, 03, 11, 00, 00), Items = items.Where(d => d.DateTime.ToShortDateString() == new DateTime(2014, 09, 03).ToShortDateString()}
};
This seems like it should be the answer to me, because I'm only interested in the date and not the time, but it doesn't seed like this. I get the error:
LINQ to Entities does not recognize the method 'System.String
ToShortDateString()' method, and this method cannot be translated into
a store expression.
Any thoughts on how to build a better solution?
UPDATE
Looks like I didn't transcribe my code accurately to the site, and ended up finding my problem.
In my original code, I had:
new Junk { JunkId = 1, DateTime = new DateTime(2014, 09, 05, 10, 00, 00), Items = context.Items.Where(d => d.DateTime.Date == new DateTime(2014, 09, 05) }
When I should have had
new Junk { JunkId = 1, DateTime = new DateTime(2014, 09, 05, 10, 00, 00), Items = items.Where(d => d.DateTime.Date == new DateTime(2014, 09, 05) }
I'd been flirting with .ToShortDateString() unnecessarily
Change filter to:
.Where(d => d.DateTime.Date == new DateTime(2014, 09, 05))
You can use TruncateTime function of EntityFunctions Class.
new Junk { JunkId = 1, DateTime = new DateTime(2014, 09, 05, 10, 00, 00), Items = items.Where(d => EntityFunctions.TruncateTime(d.DateTime) == EntityFunctions.TruncateTime(new DateTime(2014, 09, 05))}
Note: If you using EntityFramework 6 then it should be System.Data.Entity.DbFunctions.TruncateTime(...) method from EntityFramework.dll.
Your subquery (the Items = part of the Junk variable) isn't a list but an enumerable, so it doesn't get materialized untill it's enumerated, and it's not enumerated untill you use it, down the road, in your linq to entities query.
If you change it as such :
var junk= new List<Junk>()
{
new Junk { JunkId = 1, DateTime = new DateTime(2014, 09, 05, 10, 00, 00), Items = items.Where(d => d.DateTime.ToShortDateString() == new DateTime(2014, 09, 05).ToShortDateString()).ToList()}, // this one will work as it gets materialized right now, not later, so by the time you pass it to linq to entities later it will already be a simple list of diferences, it won't try to compute "toshortdatestring" later, that will already be done
new Junk { JunkId = 2, DateTime = new DateTime(2014, 09, 03, 11, 00, 00), Items = items.Where(d => d.DateTime.ToShortDateString() == new DateTime(2014, 09, 03).ToShortDateString()}
};
Getting InvalidOperationException or sometimes TargetInvocationException, When Control reaches at this function the Exception thrown.
private void setreminders()
{
DateTime Date1 = new DateTime(2014, 02, 10, 06, 00, 00);
DateTime Date2 = new DateTime(2014, 02, 10, 07, 30, 00);
Reminder _Reminder1 = new Reminder("TodoReminder")
{
BeginTime = Date1,
Title = "Diet Reminder",
Content = "Its time to wake up, Have a Glass of Water and Start Excercising like YOGA & PRANAYAMA",
};
_Reminder1.RecurrenceType = RecurrenceInterval.Daily;
ScheduledActionService.Add(_Reminder1);
Reminder _Reminder2 = new Reminder("TodoReminder")
{
BeginTime = Date2,
Title = "Diet Reminder",
Content = "Drink a Glass of Water",
};
_Reminder2.RecurrenceType = RecurrenceInterval.Daily;
ScheduledActionService.Add(_Reminder2);
}
I have already asked a different question regarding Sorting Date Time and got help from another user to pass my values. I am using a for loop like below, but definitely am wrong here because the code brings the value one by one rather than sorting.
public class Break
{
public DateTime MealStart { get; set; }
public DateTime MealEnd { get; set; }
}
my main class
IList<DateTime> starts = new List<DateTime>();
IList<DateTime> ends = new List<DateTime>();
DateTime breakStart1 = new DateTime(2012, 02, 15, 12, 30, 00); // 15/02/12 12.30PM
DateTime breakEnd1 = new DateTime(2012, 02, 15, 13, 30, 00); // 15/02/12 01.30PM
DateTime breakStart2 = new DateTime(2012, 02, 15, 11, 00, 00); // 15/02/12 11.00AM
DateTime breakEnd2 = new DateTime(2012, 02, 15, 12, 00, 00); // 15/02/12 12.00PM
DateTime breakStart3 = new DateTime(2012, 02, 15, 12, 00, 00); // 15/02/12 12.00PM
DateTime breakEnd3 = new DateTime(2012, 02, 15, 01, 00, 00); // 15/02/12 01.00PM
starts.Add(breakStart1);
starts.Add(breakStart2);
starts.Add(breakStart3);
ends.Add(breakEnd1);
ends.Add(breakEnd2);
ends.Add(breakEnd3);
for (int i = 0; i < starts.Count; i++)
{
var breaks = new List<Break>()
{
//for (int j= 0; j<starts.Count; j++)
//{
new Break()
{
MealStart = starts[i],
MealEnd = ends[i]
}
// }
};
var ordered = breaks.OrderBy(s => s.MealStart);
foreach (var ord in ordered)
{
System.Console.WriteLine(ord.MealStart);
System.Console.WriteLine(ord.MealEnd);
}
}
I am expecting a result like below
breakStart1 = 15/02/12 11.00AM
breakEnd1= 15/02/12 12.00PM
breakStart2 = 15/02/12 12.00PM
breakEnd2= 15/02/12 01.00PM
breakStart3 = 15/02/12 12.30PM
breakEnd3= 15/02/12 01.30PM
but it's not because of the for loop.
You are creating breaks after ever loop, you need to do this outside of the loop like this:
IList<DateTime> starts = new List<DateTime>();
IList<DateTime> ends = new List<DateTime>();
DateTime breakStart1 = new DateTime(2012, 02, 15, 12, 30, 00); // 15/02/12 12.30PM
DateTime breakEnd1 = new DateTime(2012, 02, 15, 13, 30, 00); // 15/02/12 01.30PM
DateTime breakStart2 = new DateTime(2012, 02, 15, 11, 00, 00); // 15/02/12 11.00AM
DateTime breakEnd2 = new DateTime(2012, 02, 15, 12, 00, 00); // 15/02/12 12.00PM
DateTime breakStart3 = new DateTime(2012, 02, 15, 12, 00, 00); // 15/02/12 12.00PM
DateTime breakEnd3 = new DateTime(2012, 02, 15, 01, 00, 00); // 15/02/12 01.00PM
starts.Add(breakStart1);
starts.Add(breakStart2);
starts.Add(breakStart3);
ends.Add(breakEnd1);
ends.Add(breakEnd2);
ends.Add(breakEnd3);
List<Break> breaks = new List<Break>();
for (int i = 0; i < starts.Count; i++)
{
breaks.Add(new Break()
{
MealStart = starts[i],
MealEnd = ends[i]
});
}
var ordered = breaks.OrderBy(s => s.MealStart);
foreach (var ord in ordered)
{
System.Console.WriteLine(ord.MealStart);
System.Console.WriteLine(ord.MealEnd);
}
Since #Corylulu beat me to the punch on the basic issue, here's a different method that is slightly shorter:
IEnumerable<Break> breaks =
starts.Zip(ends, (s, e) => new Break { MealStart = s, MealEnd = e })
.OrderBy(b => b.MealStart);
foreach (Break brk in breaks)
Console.WriteLine("Start: {0}\tEnd: {1}", brk.BreakStart, brk.BreakEnd);
The IEnumerable.Zip method takes a pair of IEnumerables and a transform function and produces an output IEnumerable containing the results of calling the transform function with members of each input IEnumerable. You could convert it to a List<Break> with a ToList() at the end of course.