Find the Maximum date in an Array (non-LINQ) - c#

Suppose I have an array of dates
DateTime[] dt = new DateTime[10];
// populate array with dates
How can I find the maximum date without using LINQ?

var maxDate = DateTime.MinValue;
foreach (var date in dt) {
maxDate = date > maxDate ? date : maxDate;
}
You mention using a sorting algorithm, presumably so that you can just ask for:
var maxDate = dt[dt.Length - 1];
After the array was sorted. The thing is, you'd need to find a sorting algorithm that performs better than O(n) to gain any advantage. Bubble Sort is O(n^2) so there's no point in using it; it performs worse, on average, than just running through the list once.

In the code below i initialise the max date to an 'empty' value, then i loop through the array and update the max date if we don't have one yet, or the one we do have is before the one in the array.
DateTime? Max = null;
foreach(var d in dt)
{
if(Max == null || Max.Value < d)
Max = d;
}
The above deals with an empty array and an actual date of DateTime.MinValue

DateTime maxDate = DateTime.MinValue;
foreach (DateTime date in dt
{
if (date > maxDate)
{
maxDate = date;
}
}

Here's a solution which uses sorting (modifies the array so that it becomes sorted):
Array.Sort(dt);
DateTime maxValue = dt[dt.Length - 1];

Related

Find a date from a specific list of date

I am a beginner in c # and I can not find the solution for my problem.
I am creating a personal project that allows me to send reminders, I have a date list and I need to do tasks between two specific dates in real life.
I found how to get the next date from today's date but I can't find how to get the previous one
Here is my sample code
void calc_x_date()
{
List<string> x_dates = new List<string>();
x_dates.Add("10/01/2017");
x_dates.Add("14/02/2017");
x_dates.Add("14/03/2017");
x_dates.Add("11/04/2017");
x_dates.Add("09/05/2017");
x_dates.Add("13/06/2017");
x_dates.Add("04/07/2017");
x_dates.Add("08/08/2017");
x_dates.Add("12/09/2017");
x_dates.Add("10/10/2017");
x_dates.Add("14/11/2017");
x_dates.Add("12/12/2017");
var allDates = x_dates.Select(DateTime.Parse).OrderBy(d => d).ToList();
var todayDate = DateTime.Today;
var nextDate = todayDate >= allDates.Last()
? allDates.Last()
: todayDate <= allDates.First()
? allDates.First()
: allDates.First(d => d >= todayDate);
string NextDate = nextDate.ToString(); // the closest next date from today
//string PreviousDate = // the closest previous date from today
}
Could someone explain me how to find my previous date please ?
Thanks in advance
I'd suggest using List<T>.BinarySearch: that will find the index of the date. If the index is 0 or more, then the exact date was found. If it's negative, then taking ~index will get you the index where the date would have been inserted.
Once you've got that information, it's easy to find the value at the previous index or the next index - but you should consider all the cases of:
Today is before all dates
Today is after all dates
Today is a date between the first and last date in the list, but isn't in the list
Today is equal to the first date
Today is equal to the last date
Today is equal to a date in the list which isn't the first or last date
As asides:
I would strongly encourage you to get in the habit of following .NET naming conventions as early as possible
I'd encourage you not to use strings to represent dates unless you really need to
If you're doing a lot of date/time work, you may find my Noda Time library easier to use correctly than DateTime
Here is a Linq approach
List<string> x_dates = new List<string>();
x_dates.Add("10/01/2017");
x_dates.Add("14/02/2017");
x_dates.Add("14/03/2017");
x_dates.Add("11/04/2017");
x_dates.Add("09/05/2017");
x_dates.Add("13/06/2017");
x_dates.Add("04/07/2017");
x_dates.Add("08/08/2017");
x_dates.Add("12/09/2017");
x_dates.Add("10/10/2017");
x_dates.Add("14/11/2017");
x_dates.Add("12/12/2017");
DateTime today = DateTime.Today;
IEnumerable<DateTime> dt_dates = x_dates.Select(DateTime.Parse);
DateTime prev = dt_dates.Where(x => x < today)
.OrderByDescending(x => x)
.First();
DateTime next = dt_dates.Where(x => x > today)
.OrderBy(x => x)
.First();
alternative solution
DateTime prev = dt_dates.Where(x => x < today).Max();
DateTime next = dt_dates.Where(x => x > today).Min();
Storing dates in string format works. It is however incredibly difficult to do date comparisons. You have to first cast it to numbers, handle the exceptions, etc.
C# has a DateTime object. You can store dates in this and ignore the time. DateTime objects can be compared using the < and > operators.
If you create a class with a start date and an end date, store these objects in a list(of tasks), would that solve your problem? You can also add a text of the task in a string to said object.

How to get dates between two dates in C#

I would like to get dates between two dates. Instead of expected 9 different dates, I get 875287 and run out of memory. What would be the problem with the code below?
StartDate value is 01/04/2016 00:00:00
EndDate value is 10/04/2016 00:00:00
var selectedDates = new List<DateTime?>();
for (var date = StartDate; date <= EndDate; date.Value.AddDays(1))
{
selectedDates.Add(date);
}
You aren't assigning the value of date.Value.AddDays(1) to anything, so it ends up in an infinite loop. You'd need to change your code so that date is set to the result of AddDays.
for (var date = StartDate; date <= EndDate; date = date.AddDays(1))
{
selectedDates.Add(date);
}
LINQ solution (let's generate selectedDates):
var selectedDates = Enumerable
.Range(0, int.MaxValue)
.Select(index => new DateTime?(StartDate.AddDays(index)))
.TakeWhile(date => date <= EndDate)
.ToList();
As far as I can see, since AddDays method returns a new instance of a DateTime, it does not change the current instance since DateTime is immutable.
Looks like your date is DateTime?, you can change this part as;
for (var date = StartDate; date <= EndDate; date = date.Value.AddDays(1))
{
selectedDates.Add(date);
}
As usr pointed, this might be affected on DST. You might wanna check Dmitry's answer as well.
A shorter notation using Linq's Range method uses the ability to already figure out the number of days using the TimeSpan.Days property after subtracting start from end.
Assuming the start is before end you'd end up with:
DateTime StartDate = new DateTime(1979, 10, 4);
DateTime EndDate = new DateTime(2016, 10, 4);
var dates = Enumerable.Range(0, (EndDate - StartDate).Days + 1)
.Select(day => StartDate.AddDays(day))
If you need it to be Nullable, add:
.Cast<DateTime?>()
If you need this to be a List, add:
.ToList()
It's probably quite a bit more efficient than the other LINQ based solution.
Decided to change it up with a do/while
var selectedDates = new List<DateTime?>();
DateTime? StartDate = DateTime.Parse("01/04/2016 00:00:00");
DateTime? EndDate = DateTime.Parse("10/04/2016 00:00:00");
do
{
selectedDates.Add(StartDate);
StartDate = StartDate.Value.AddDays(1);
}while(StartDate < EndDate);

Find next closest date

I have some dates which are currently stored as a list of strings.
For example:
List<string> dates = new List<string>();
dates.Add("1/10/14");
dates.Add("2/9/14");
dates.Add("1/15/14");
dates.Add("2/3/14");
dates.Add("2/15/14");
(The date is in mm/dd/yy format)
I will take a user's input (also in mm/dd/yy format), but as a string.
Now, I want to find the date in the array that is the next closest after the user input date.
For example, if the user enters "1/13/14", the output should be "1/15/14".
If the user enters "2/5/14", then the next closest date is "2/9/14".
But if the user enter a date that is later than the last date (say "3/1/14", it will STILL return the last date in the array which is "2/15/14")
I know at some point you have to convert to type DateTime, but I couldn't figure out the logic to find such date.
List<string> dates = new List<string>();
dates.Add("1/10/14");
dates.Add("2/9/14");
dates.Add("1/15/14");
dates.Add("2/3/14");
dates.Add("2/15/14");
var allDates = dates.Select(DateTime.Parse).OrderBy(d=>d).ToList();
var inputDate = DateTime.Parse("1/13/14");
var closestDate = inputDate >= allDates.Last()
? allDates.Last()
: inputDate <= allDates.First()
? allDates.First()
: allDates.First(d => d >= inputDate);
For now I'm just parsing strings, but you should handle it separately. This is simple plain LINQ, you can go fancy and do binary search as well.
Here is a solution that uses a Binary Search.
You need to have a list of DateTimes, no question about it. No point in keeping them as strings, parse them on the way into the list if you have to. You can use LINQ to convert all the elements in the list, but that's the only way you'll be able to compare dates with eachother.
Check out the page for BinarySearch to understand why I am using the bitwise operator on the return value.
//build list of random dates
Random r = new Random();
var dates = new List<DateTime>();
for (int i = 0; i < 10; i++)
{
dates.Add(new DateTime(2014, r.Next(1,13), r.Next(1,28)));
}
//sort list (if you don't do this, you'll most likely get the wrong answer)
dates.Sort();
//get input
string input = "1/13/14";
DateTime inputDate = DateTime.Parse(input);
//find nearest
var result = dates.BinarySearch(inputDate);
DateTime nearest;
//get match or next in list.
if(result >= 0)
nearest = dates[result];
else if (~result == dates.Count )
nearest =dates.Last();
else
nearest = dates[~result];
If you need to find the true closest, use this in place of the last if block.
//get match, or true nearest date in list
if(result >= 0) //date was found! Use that index.
nearest = dates[result];
else if (~result == 0) //date not found, smaller than any item in the list. use first index.
nearest = dates.First();
else if(~result == dates.Count) //date was not found, and is greater than all items. Use last index.
nearest = dates.Last();
else //date not found, somewhere in the middle of the list. find the nearest date
{
var daysAfter = dates[~result].Subtract(inputDate); //date after input
var daysBefore = inputDate.Subtract(dates[~result - 1]); //date before input
if(daysAfter < daysBefore)
nearest = dates[~result];
else
nearest = dates[~result - 1];
}
DateTime searchDate = new DateTime(2014,03,01);
var orderedDates = dates
.Select(d => DateTime.ParseExact(d, "M/d/yy", CultureInfo.InvariantCulture))
.OrderBy(d => d).ToList();
var result = orderedDates.FirstOrDefault(d => d > searchDate);
if (result == default(DateTime))
result = orderedDates.Last();
Something like this?
List<string> dates = YourListOfStringsHere() ; // list of strings in MM/dd/yyyy form.
DateTime desiredDate = new DateTime(2014,2,27) ;
DateTime foundDate = dates
.Select( x => DateTime.ParseExact(x,"MM/dd/yyyy",CultureInfo.CurrentCulture))
.Where( x => x >= desiredDate.Date )
.Min()
;
Why you'd be storing date/time values as a string is beyond me. When you load them, why not convert them? That way, they're easier to manipulate, you know you've got clean data. Everything becomes much simpler.

Get all months and years between two datetime

What I need is to get logic on how to get monthname-year between two dates.
Dictionary<Monthname,year> GetMonthsandYear(Datetime d1,Datetime d2)
or
List<Tuple<string,int> GetMonthsandYear(Datetime d1,Datetime d2)
example : jan-1-2013 to mar-3-2013
should return January-2013,february-2013,march-2013 or in reverse format by list.reverse
If your actual requirement is "the previous 24 months" then it's much simpler. Just start off with the current month, and get the 1st day of it - then iterate and add 1 month 24 times.
Personally I'd return an IEnumerable<DateTime> rather than anything else - you can format each element however you want - but it's pretty simple:
public static IEnumerable<DateTime> GetMonths(int count)
{
// Note: this uses the system local time zone. Are you sure that's what
// you want?
var today = DateTime.Today;
// Always return the 1st of the month, so we don't need to worry about
// what "March 30th - 1 month" means
var startOfMonth = new DateTime(today.Year, today.Month, 1);
for (int i = 0; i < count; i++)
{
yield return startOfMonth;
startOfMonth = startOfMonth.AddMonths(-1);
}
}
Then if you want a List<string> of these values as "February 2014" etc for example, you could have:
var monthYears = GetMonths(24).Select(dt => dt.ToString("MMMM yyyy"))
.ToList();
Note that a Dictionary<...> would not be appropriate unless you really don't care about the order - and I suspect you do. You shouldn't rely on the order in which items are returned from a Dictionary<TKey, TValue> when you view it as a sequence - it's not intended to be an ordered collection.
I don't understand why you need Dictionary or List<Tuple<string,int> but one solution could be;
DateTime dt1 = new DateTime(2013, 1, 1);
DateTime dt2 = new DateTime(2013, 3, 3);
while (dt1 < dt2)
{
Console.WriteLine(dt1.ToString("MMMM-yyyy"));
dt1 = dt1.AddMonths(1);
}
Result will be;
January-2013
February-2013
March-2013
Even if you need, you can add these values to a List<string> in while loop.
But be carefull about what Jon said, this solution will generate only January and February if your dt1.Day is greater than dt2.Day.

Iterate between two dates in C#

I have two dates:
DateTime fromDate = new DateTime(2013,7,27,12,0,0);
DateTime toDate = new DateTime(2013,7,30,12,0,0);
I want to iterate from fromDate to toDate by incrementing fromDate with a single day and the loop should break when fromDate becomes equal to or greater than the toDate. I have tried this:
while(fromDate < toDate)
{
fromDate.AddDays(1);
}
But this is an infinite loop and won't stop. How can I do this ?
Untested but should work:
for(DateTime date = fromDate; date < toDate; date = date.AddDays(1)) {
}
Modify the comparison to <= if you want to include toDate as well.
DateTime.AddDays indeed adds the specified number of days to the date - but the resulting date is returned as a new DateTime value; the original DateTime value is not changed.
Therefore, make sure you assign the result of the your operation back to the variable you inspect in your loop condition:
while (fromDate < toDate)
{
fromDate = fromDate.AddDays(1);
}

Categories