Lowest date from list after remove below DateTime.Now? - c#

Hey I want to find the lowest date after DateTime.Now from my saved times. My Idea was to add them all to a list then delete all 'DateTimes' from the list which are lower than the actual time to then find the lowest date in the list.
The way I approached it doesnt work and I dont really understand why..
Here's my code:
List<DateTime> list = new List<DateTime>() {
Convert.ToDateTime(search.ReadConfig("somedate1")),
Convert.ToDateTime(search.ReadConfig("somedate2")),
Convert.ToDateTime(search.ReadConfig("somedate3")),
Convert.ToDateTime(search.ReadConfig("somedate5"))};
DateTime dtnow = DateTime.Now;
for(int kk = 0; kk < list.Count; kk++){
if(list[kk] < dtnow){ list.RemoveAt(kk); }
}
DateTime smallestDate = list.Min();
label2.Text = smallestDate.ToString();
It doesnt remove the times older than now and I wasnt able to find something which fits my problem over google.
I would appreciate any help!

Try Where() clause,
Filters a sequence of values based on a predicate.
//Here you have to use Greater than equal operator.
var minDate = list.Where(x => x >= DateTime.Now).Min().ToString();
Console.WriteLine(minDate);

Related

Find the closest previous DateTime from a list of DateTime

Is there a faster way of obtaining the closest previous (past) DateTime from a list of DateTimes when compared to a specific time? (the list comes from a SQL database)
public DateTime GetClosestPreviousDateTime(List<DateTime> dateTimes, DateTime specificTime)
{
DateTime ret = null;
var lowestDifference = TimeSpan.MaxValue;
foreach (var date in dateTimes)
{
if (date >= specificTime)
continue;
var difference = specificTime- date;
if (difference < lowestDifference)
{
lowestDifference = difference;
ret = date;
}
}
return ret;
}
The source list will be sorted since the dates in the list come from a SQL database where they are written consecutively.
It depends what you mean by "faster". The algorithm you show is O(N) so no you won't get faster than that - if by faster you mean is there a way to not have to iterate over all dates.
But if you mean can you shave off a few microseconds with some code that doesn't emit quite as many op codes, then yes of course. But is that really the issue here?
The answer will also change based on the size of the list, how accurate you need the answer to be, whether we can make any assumptions on the data (e.g. is it already sorted).
dateTimes.Sort();
var closest = dateTimes[dateTimes.IndexOf(search) - 1];
Your problem is a classic search algorithm and binary search might suit you.
Sort list: dateTimes.Sort();
Apply Binary Search algo with similar logic as in your for statement.
dateTimes.Where(x => x < specificTime).Max()
or if you want to handle the case where none exist:
dateTimes.Where(x => x < specificTime).DefaultIfEmpty().Max()
Later edit: Now you introduce new information that the List<> is already sorted. That was not in the question before.
With a sorted List<>, your algorithm is silly since it foreaches on and on, even after having reached the point where the entries "pass" the threshold specificTime. You can use instead BinarySearch (assuming List<> is sorted in ascending order and contains no duplicates):
static DateTime GetClosestPreviousDateTime(List<DateTime> dateTimes, DateTime specificTime)
{
var search = dateTimes.BinarySearch(specificTime);
var index = (search < 0 ? ~search : search) - 1;
if (index == -1)
throw new InvalidOperationException("Not found");
return dateTimes[index];
}
If you want to do it faster, just ask the database for the value, it will know how to find the answer fast; do not fetch the entire List<> to memory first. Use SQL or LINQ to the database.

Find missed elements in array of numbers

I have array with numbers 1...31 (days at month)
On my side I would like to find all missed numbers.
This my solution but I not sure that is a good way.
var result = new List<int>();
int[] days = GetSelectedDays(); //I recive array with 1..31 with some missing elements sometimes (without "5" as example it depends if user has selected this number)
for (int i=0; i <30; i++)
{
if (!days.Contains(i))
result.Add(i);
}
You can use LINQ Except:
var result = Enumerable.Range(1, 31).Except(days);
Looks like you are currently checking numbers in the range 0..29 as opposed to 1..31. The only suggestion I have is to change the for loop to:
for (int i=1; i <= 31; i++)
First off, when you're adding days of a month its probably easier to loop through a loop of DateTimes, i.e.
var missedDateDictionary = new Dictionary<DateTime, bool>(); // or other value
var date = new DateTime(2016, 01, 01);
for (int i = 0 ; i < 32 ; i++)
{
var newDate = date.AddDays(i);
//do something with adding / not adding date
if (notDoneSomething)
missedDateDictionary.Add(newDate, true)
}
The premise here is that each DateTime you're adding is unique.
If you cant guarantee that, your solution is pretty efficient but could use some optimization.
Initialize arary with max value: int[31] days = GetSelectedDays();
A month can have 31 days: for (int i = 1; i <32; i++)
If at all possible, fill result in the loop you're filling GetSelecteDays()

searching results of ordered LINQ query

my LINQ query returns an ordered sequence of calendar dates, and i need to output this sequence starting from the earliest date that is more that the given number of days apart from the starting date of the sequence.
the code below does that using linear search. it seems that i could use a binary search to find the beginning date if LINQ query supported this.
in this contrived example i can search the list but in my real code i am trying to avoid storing the whole sequence in memory and i prefer to use just IEnumerable.
any ideas how to make it more efficient? i have thousands of items in my query and doing linear search is just lame...
thanks
konstantin
using System;
using System.Collections.Generic;
using System.Linq;
namespace consapp
{
static class Program
{
static void Main(string[] args)
{
var dates = new List<DateTime>();
var xs = dates.OrderBy(x => x);
dates.Add(DateTime.Parse("11/10/11"));
dates.Add(DateTime.Parse("02/02/11"));
dates.Add(DateTime.Parse("11/24/11"));
dates.Add(DateTime.Parse("09/09/11"));
dates.Add(DateTime.Parse("11/10/11"));
var d = DateTime.MinValue;
double offset = 1.2;
foreach (var x in xs)
{
if (d != DateTime.MinValue)
{
offset -= (x - d).Days;
}
if (offset < 1)
{
Console.WriteLine(x.ToShortDateString());
}
d = x;
}
}
}
}
Binary search would likely be better if your data set is pre-sorted or you do not know the start date of your sequence ahead of time. However if you are sorting your dates using OrderBy like your example and you do know the start date of the sequence, why not put a Where clause in to filter out the dates that don't meet your criteria before you order the sequence?
var xs = from date in dates
where (date - target).Days < 1.2
order by date
select date;
If you have sorted dates IEnumerable in sortedData collection, then here how you can get select dates later than a threshold from the first date:
var threshold = TimeSpan.FromDays(1);
var filteredDates = sortedDates.SkipWhile(sd => sd - sortedDates.First() <= theshold);
It has the advantage over .Where that it only need to go check first dates till it gets to threshold. After that it's just enumerating elements.
Note that it's IEnumerate, so you get all the benefits of lazy loading

Fastest way to check a List<T> for a date

I have a list of dates that a machine has worked on, but it doesn't include a date that machine was down. I need to create a list of days worked and not worked. I am not sure of the best way to do this. I have started by incrementing through all the days of a range and checking to see if the date is in the list by iterating through the entire list each time. I am looking for a more efficient means of finding the dates.
class machineday
{
datetime WorkingDay;
}
class machinedaycollection : List<machineday>
{
public List<TimeCatEvent> GetAllByCat(string cat)
{
_CategoryCode = cat;
List<machineday> li = this.FindAll(delegate(machinedaydummy) { return true; });
li.Sort(sortDate);
return li;
}
int sortDate(machinedayevent1, machinedayevent2)
{
int returnValue = -1;
if (event2.date < event1.date)
{
returnValue = 0;
}
else if (event2.date == event1.date)
{
//descending
returnValue = event1.date.CompareTo(event2.date);
}
return returnValue;
}
}
Sort the dates and iterate the resulting list in parallel to incrementing a counter. Whenever the counter does not match the current list element, you've found a date missing in the list.
List<DateTime> days = ...;
days.Sort();
DateTime dt = days[0].Date;
for (int i = 0; i < days.Length; dt = dt.AddDays(1))
{
if (dt == days[i].Date)
{
Console.WriteLine("Worked: {0}", dt);
i++;
}
else
{
Console.WriteLine("Not Worked: {0}", dt);
}
}
(This assumes there are no duplicate days in the list.)
Build a list of valid dates and subtract your machine day collection from it using LINQ's Enumerable.Except extension method. Something like this:
IEnumerable<DateTime> dates = get_candidate_dates();
var holidays = dates.Except(machinedays.Select(m => m.WorkingDay));
The get_candidate_dates() method could even be an iterator that generates all dates within a range on the fly, rather than a pre-stored list of all dates.
Enumerable's methods are reasonably smart and will usually do a decent job on the performance side of things, but if you want the fastest possible algorithm, it will depend on how you plan to consume the result.
Sorry dudes, but I do not pretty much like your solutions.
I think you should create a HashTable with your dates. You can do this by interating only once the working days.
Then, you interate the full range of of days and for every one you query in the hashtable if the date is there or not, by using
myHashTable.ContainsKey(day); // this is efficient
Simple, elegant and fast.
I think your solution uses an exponencial time, this one is lineal or logarithmical (which is actually a good thing).
Assuming the list is sorted and the machine was "working" most of the time, you may be able to avoid iterating through all the dates by grouping the dates by month and skipping the dates in between. Something like this (you'll need to clean up):
int chunksize = 60; // adjust depending on data
machineday currentDay = myMachinedaycollection[0];
for (int i = 0; i < myMachinedaycollection.Count; i += chunksize)
{
if (currentDay.WorkingDay.AddDays(chunksize) != myMachinedaycollection[i + chunksize].WorkingDay)
{
// write code to iterate through current chunk and get all the non-working days
}
currentDay = myMachinedaycollection[i + chunksize];
}
I doubt you want a list of days working and not working.
Your question title suggests that you want to know whether the system was up on a particular date. It also seems reasonable to calculate % uptime. Neither of these requires building a list of all time instants in the interval.
Sort the service times. For the first question, do BinarySearch for the date you care about and check whether the preceding entry was the system being taken offline of maintenance or put back into service. For % uptime, take the (down for maintenance, service restored) pair-wise, use subtraction to find the duration of maintenance, add these up. Then use subtraction to find the length of the total interval.
If your question didn't actually mean you were keeping track of maintenance intervals (or equivalently usage intervals) then you can ignore this answer.

Find the closest time from a list of times

So, here's the scenario. I have a file with a created time, and I want to choose a time from a list of times that that file's created time is closest or equal too...what would be the best way to accomplish this?
var closestTime = listOfTimes.OrderBy(t => Math.Abs((t - fileCreateTime).Ticks))
.First();
If you don't want the performance overhead of the OrderBy call then you could use something like the MinBy extension method from MoreLINQ instead:
var closestTime = listOfTimes.MinBy(t => Math.Abs((t - fileCreateTime).Ticks));
Something like this:
DateTime fileDate, closestDate;
ArrayList theDates;
long min = long.MaxValue;
foreach (DateTime date in theDates)
if (Math.Abs(date.Ticks - fileDate.Ticks) < min)
{
min = Math.Abs(date.Ticks - fileDate.Ticks);
closestDate = date;
}
The accepted answer is completely wrong. What you want is something like this:
DateTime fileDate, closestDate;
List<DateTime> theDates;
fileDate = DateTime.Today; //set to the file date
theDates = new List<DateTime>(); //load the date list, obviously
long min = Math.Abs(fileDate.Ticks - theDates[0].Ticks);
long diff;
foreach (DateTime date in theDates)
{
diff = Math.Abs(fileDate.Ticks - date.Ticks);
if (diff < min)
{
min = diff;
closestDate = date;
}
}
var closestTime = (from t in listOfTimes
orderby (t - fileInfo.CreationTime).Duration()
select t).First();
How often will you be doing this with the same list of times? If you're only doing it once, the fastest way is probably to just scan through the list and keep track of the closest time you've seen yet. When/if you encounter a time that's closer, replace the "closest" with that closer time.
If you're doing it very often, you'd probably want to sort the list, then use a binary search.
get the difference of your file creatime and every time in your list and sort the absolute value of each time difference. the first one should be the answer you are looking for.
Use the minimum absolute time difference between the file time and the time in the list. You might get 2 entries being the same, and then you would need a different method to differ between these.
Not an answer, but a question regarding the various LINQ solutions proposed above. How efficient is LINQ? I have not written any "real" programs with LINQ yet, so I'm not sure on the performance.
In this example, the "listOfTimes" collection implies that we have already iterated over some file system based objects to gather the times. Would it have been more efficient to do the analysis during the iteration instead of later in LINQ? I recognize that these solutions may be more "elegant" or nicely abstract the "collection as database" idea, but I tend to choose efficiency (must be readable though) over elagance in my programming. Just wondering if the cost of LINQ might outweigh the elegance here?
var creationTimes = new [] {DateTime.Now.AddDays(-1), DateTime.Now.AddDays(-2)};
FileInfo fi = new FileInfo("C:/test.xml");
var closestTime = creationTimes
.OrderBy(c => Math.Abs(c.Subtract(fi.CreationTime).Days))
.First();
var min = listoftimes.Select(
x => new { diff = Math.Abs((x - timeoffile).Ticks), time = x}).
OrderBy(x => x.diff).
First().time;
Note: Assumes at least 1 entry in listoftimes.
I thought I would update this post to include a real world scenario. I wanted this sort of function as I have a blog showing news of the latest movie screenings.
However I don't want to list screening in the past (ie screening date past the current date) and as I wanted to show a record I needed some sort of ID passed to pick up the record.
I have left if simple so that you can follow the process and no doubt make it more efficient with LINQ et al.
First the model
public class LatestScreeeningsModel
{
public int Id { get; set; }
public DateTime Date { get; set; }
}
Then the code block you can call from your controller
private static LatestScreeeningsModel GetLatestScreening(IPublishedContent currentNode)
{
LatestScreeeningsModel latestScreening = new LatestScreeeningsModel();
DateTime fileDate;
// get a list of screenings that have not shown yet
var screenings = currentNode.AncestorsOrSelf("siteLanguage")
.FirstOrDefault().Descendants("screening")
.Select(x => new LatestScreeeningsModel() { Id = x.Id, Date = x.GetPropertyValue<DateTime>("date") })
.Where(x => x.Date > DateTime.Now).ToList();
fileDate = DateTime.Today;
long min = Math.Abs(fileDate.Ticks - screenings[0].Date.Ticks);
long diff;
foreach (var comingDate in screenings)
{
diff = Math.Abs(fileDate.Ticks - comingDate.Date.Ticks);
if (diff <= min)
{
min = diff;
latestScreening = comingDate;
}
}
return latestScreening;
}
I am using Umbraco to get the date items but it would work with any custom model, List et al.
Hope it helps
This is a generalized solution to the question, "Find the closest time from a list of times". This solution finds the closest time before and after a given search time.
//For finding the closest time in a list using a given search time...
var listOfTimes = new List<DateTime>();
listOfTimes.Add(DateTime.Parse("1/1/2000"));
listOfTimes.Add(DateTime.Parse("1/2/2000"));
listOfTimes.Add(DateTime.Parse("1/3/2000"));
listOfTimes.Add(DateTime.Parse("1/4/2000"));
listOfTimes.Add(DateTime.Parse("1/5/2000"));
var searchTime = DateTime.Parse("1/3/2000");
var closestBefore = listOfTimes.LastOrDefault(t => t < searchTime);
var closestAfter = listOfTimes.FirstOrDefault(t => t > searchTime);
Console.WriteLine(closestBefore);
Console.WriteLine(closestAfter);
/*
searchTime: 1/3/2000
before: 1/2/2000 12:00:00 AM
after: 1/4/2000 12:00:00 AM
searchTime: 1/1/1900 (edge case)
before: 1/1/0001 12:00:00 AM (DateTime.MinValue)
after: 1/1/2000 12:00:00 AM
searchTime: 1/1/2001 (edge case)
before: 1/5/2000 12:00:00 AM
after: 1/1/0001 12:00:00 AM (DateTime.MinValue)
*/

Categories