Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LINQ Query - nearest adjacent date

Tags:

c#

.net

linq

I am trying to write a method that will determine the closest date chronlogically, given a list of dates and a target date. For example, given a (simplified) date set of {Jan 2011, March 2011, November 2011}, and a target date of April 2011, the method would return March 2011

At first, I was thinking of using LINQ's skip, but I'm not sure of an appropriate Func such that it would stop before the date was exceeded. This below seems a viable solution, but I'm not sure if there's a more efficient means of doing this. Presumably Last and First would be linear time each.

The source dataSet can be between 0 and 10,000 dates, generally around 5,000. Also, I am iterating over this whole process between 5 and 50 times (this is the number of target dates for different iterations).

// assume dateSet are ordered ascending in time.
public DateTime GetClosestDate(IEnumerable<DateTime> dateSet, DateTime targetDate)
{
   var earlierDate = dateSet.Last(x => x <= targetDate);
   var laterDate = dateSet.First(x => x >= targetDate);

   // compare TimeSpans from earlier to target and later to target.
   // return closestTime;
}
like image 586
Stealth Rabbi Avatar asked Aug 10 '11 19:08

Stealth Rabbi


1 Answers

Well, using MinBy from MoreLINQ:

var nearest = dateSet.MinBy(date => Math.Abs((date - targetDate).Ticks);

In other words, for each date, find out how far it is by subtracting one date from the other (either way round), taking the number of Ticks in the resulting TimeSpan, and finding the absolute value. Pick the date which gives the smallest result for that difference.

If you can't use MoreLINQ, you could either write something similar yourself, or do it in two steps (blech):

var nearestDiff = dateSet.Min(date => Math.Abs((date - targetDate).Ticks));
var nearest = dateSet.Where(date => Math.Abs((date - targetDate).Ticks) == nearestDiff).First();
like image 56
Jon Skeet Avatar answered Oct 07 '22 22:10

Jon Skeet