English 中文(简体)
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?

最佳回答

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;
 }
问题回答
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));
var closestTime = (from t in listOfTimes
                   orderby (t - fileInfo.CreationTime).Duration()
                   select t).First();

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;
    }
  }

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?

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)
*/

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





相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签