Finding missing dates from a collection - c#

I've dates array with values below:
"07/07/2011", "08/05/2011", "09/07/2011", "12/07/2011"
Using this as input in my C# program, I need to build a new collection which will have missing dates..ie. 10/07/2011, 11/07/2011.
Is recursion the best way to achieve this?
Thanks.

Not at all. This should be a straightforward process. You have a starting date, you have an interval... You start walking the array and if the next value does not match your previous value plus the interval you insert a new value into the new array. If it does match, you copy that value.
If you need more data (metadata) about each entry then create a class that holds the date and whatever metadata you find useful (e.g. a bool like this_value_was_inserted_artificially)
Using recursion would unnecessarily complicate things.

No recursion needed. This can probably be optimized, but should do the job:
public static IEnumerable<DateTime> FindMissingDates(IEnumerable<DateTime> input)
{
// get the range of dates to check
DateTime from = input.Min();
DateTime to = input.Max();
// how many days?
int numberOfDays = to.Subtract(from).Days;
// create an IEnumerable<DateTime> for all dates in the range
IEnumerable<DateTime> allDates = Enumerable.Range(0, numberOfDays)
.Select(n => from.AddDays(n));
// return all dates, except those found in the input
return allDates.Except(input);
}

You can pull this off very nicely with Linq:
var dates=new[]{
DateTime.Parse("07/07/2011"),
DateTime.Parse("08/07/2011"),
DateTime.Parse("09/07/2011"),
DateTime.Parse("12/07/2011")};
var days=(dates.Max()-dates.Min()).Days;
var otherDays=
Enumerable
.Range(0,days)
.Select(d=>dates.Min().AddDays(d))
.Except(dates);

Related

How to compare two lists in C#?

I have two lists to compare:
I want to compare List A and List B such that if any of the dates from ListB is present in List A then return true.
For example, if 14-01-2020 (which is in List B) is present in List A (which is definitely present) then it should return true.
How to do that?
Please note: The data in List A contains the dates of an entire month, whereas List B contains only a few dates.
If any of the dates from ListB is present in List A then return true.
return ListB.Any(x => ListA.Contains(x));
or vice versa:
return ListA.Any(x => ListB.Contains(x));
Which one is better for you will depend on the nature of your data, but I'd normally favor running Contains() over the shorter sequence.
Additionally, I see this:
The data in List A contains the dates of an entire month
Depending on exactly what you mean, you may be able to take advantange of that fact:
var start = A.Min(x => x);
var stop = A.Max(x => x);
return ListB.Any(x => x >= start && x <= stop);
Finally, if you know the data in one or both of the sequences is sorted, you can optimize these significantly.

How to find nearest DateTime keys of DateTime in SortedDictionary?

I have a SortedDictionary of DateTime and double _historicalValues, and a method HistoricalValue which is passed a DateTime date. HistoricalValue must return the corresponding double for the DateTime in _historicalValues that is closest to date. Is there a nice way in which I can leverage a SortedDictionary in order to find the closest DateTime?
I think there is a better way to approach this task as my approach is something that doesn't leverage a SortedDictionary at all, and tediously has to iterate over the whole collection to evaluate the date differences first.
private readonly SortedDictionary<DateTime, double> _historicalValues;
public double HistoricalValue(DateTime date)
{
if (_historicalValues.ContainsKey(date))
return _historicalValues[date];
var closestDate = _historicalValues.Keys.OrderBy(
t => Math.Abs((t - date).Ticks)).First();
return _historicalValues[closestDate];
}
I would use the MinBy mentioned in this answer: enter link description here
And then use it like this:
return _historicalValues.MinBy(kvp => Math.Abs((kvp.Key - date).Ticks)).Value;

Cast DateTime to Unix Timestamp in Select statement?

I do have a database table with millions of archived value rows that consist of a value (single), two integer id fields, and a datetime field (and some other fields that are not relevant for the cache I want to build). The table structure is fixed, I can't change it. Now I want to read all rows into an array of objects of a simple class that I want to keep in memory for caching purposes.
In order to keep the memory consumption low, I want to use a unix timestamp instead of a datetime object. This is also useful because the frontend charts etc. that will consecutively work with this cache also natively work with Unix Timestamps.
For the cache creation I want to select directly into an ArchiveElement. This works well for most fields, but I don't know how to create a Unix Timestamp on the fly WITHIN the select statement:
ArchiveCache = db.ArchiveValues.Select(x => new ArchiveElement() {
DataPointId = (UInt16)x.DataPointId,
StationId = (UInt16)x.StationId,
Value = (Single)x.Value,
DateValue = x.DateValue // <-- Here I want to cast into Unix Timestamp
});
This is the ArchiveElement class:
public class ArchiveElement
{
public UInt32 DateValue;
public UInt16 DataPointId;
public UInt16 StationId;
public Single Value;
}
I do have a function in my application that can convert a DateTime into a Unix TimeStamp, but that does not work within the scope of the select statement. So I need to find another way.
One way would be to insert a .ToList() before the .Select(..) statement, so that I have access to my own function, but that would be an ugly workaround as it fetches a lot of unneccessary fields from the DB.
Is there a way to somehow convert the datetime "in place"?
So first you declare the Unix Epoch time:
var unixEpoch = DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
Now in your Select, just subtract the epoch from your date value which gives a TimeSpan type from which you can get the TotalSeconds which is the value you need:
ArchiveCache = db.ArchiveValues.Select(x => new ArchiveElement() {
DataPointId = (UInt16)x.DataPointId,
StationId = (UInt16)x.StationId,
Value = (Single)x.Value,
DateValue = (x.DateValue - unixEpoch).TotalSeconds
});
Note: This assumes that your DateValue property is a double, you may need to cast it to a long.
Edit
To cope with Entity Framework, this may work:
ArchiveCache = db.ArchiveValues.Select(x => new ArchiveElement() {
DataPointId = (UInt16)x.DataPointId,
StationId = (UInt16)x.StationId,
Value = (Single)x.Value,
DateValue = SqlFunctions.DateDiff("ss", unixEpoch, x.DateValue)
});
This may require a reference/import for System.Data.Entity.SqlServer.
DATEDIFF(SECOND,{d '1970-01-01'}, GETUTCDATE())
This will return the unix seconds in sql.

Linq to Objects DateTime Except ignore time

I have a 2 List<DateTime> and want to return the subset of the first list that isn't in the second list but ignore the time.
Is it possible to do this using the Except extension method?
var missingDates = dateList1.Except(dateList2);
Would I need to set the time in both lists to 00:00:00 or is there some way I can use the .Date property to compare?
Try this:
var missingDates = dateList1.Select(x=>x.Date).Except(dateList2.Select(y=>y.Date));
Though this ignores the time completely, meaning that in your result the time will be missing, too...
On a second thought: You could of course implement a custom IEqualityComparer to preserve the time in the result:
public class DateComparer : IEqualityComparer<DateTime>
{
public bool Equals(DateTime left, DateTime right)
{
return left.Date.Equals(right.Date);
}
public int GetHashCode(DateTime dateTime)
{
return dateTime.Date.GetHashCode();
}
}
And then use that comparer:
var missingDates = dateList1.Except(dateList2, new DateComparer());
This will ignore the time in the comparison but return the dates with times intact:
List<DateTime> missingDates = dateList1.Where(list1Item => !dateList2.Exists(list2Item => list2Item.Date == list1Item.Date)).ToList();

System.OutOfMemoryException thrown when computing date ranges

It might be a simple fix, but I can't for the life of me think of how to do this. I compute a bunch of StartDates and End Dates into a bunch of arrays of dates using this query:
this.Reserved = unit.Reservations.Where(r => r.Active.HasValue && r.Active.Value).SelectMany(r => Utilities.DateRangeToArray(r.StartDate, r.EndDate)).ToArray();
Utilities.DateRangeToArray() is defined as follows:
public static IEnumerable<DateTime> DateRangeToArray(DateTime start, DateTime end) {
DateTime curDate = start;
while (curDate <= end) {
yield return curDate;
curDate.AddDays(1);
}
}
Is there a way to make this less memory intensive?
Thanks!
Your code is broken - AddDays doesn't change the existing value, it returns a new value. You're ignoring that new value, thus creating an infinite loop.
Change your code to:
public static IEnumerable<DateTime> DateRangeToArray(DateTime start,
DateTime end) {
DateTime curDate = start;
while (curDate <= end) {
yield return curDate;
curDate = curDate.AddDays(1);
}
}
Another hint: unit testing can help you find this sort of problem long before you try to use the method in a LINQ query. I'd also change the name, given that it's not returning an array.
You're sure you don't have any reservations where r.StartDate > r.EndDate, right? If you do, you'll get an infinite loop, I think.
I assume the out of memory is when converting the result to the array. Two points:
The output will contain duplicate dates for overlapping reservations.
Perhaps Reserved should be a collection of date ranges (start,end) rather than containing every date?

Categories

Resources