1

I have a List where UserObj has a DateTime data member and a string Name member. I want to find the UserObj in the list with given Name InputName and with DateTime closest to an input DateTime InputDT

The Names and DateTimes may both occur but there will be a unique solution.

I thought about:

UserObj user = userObjList.Where(u => ((u.Name ==inputName) && (userObjList.Min())).ToList()[0];

But not sure how to specify the minimum condition?

gwizardry
  • 501
  • 1
  • 6
  • 19
  • Also this question seems relevant for the minimum part: http://stackoverflow.com/questions/914109/how-to-use-linq-to-select-object-with-minimum-or-maximum-property-value . There are a couple of other possible answers there. – Chris May 16 '12 at 14:22

2 Answers2

8

Sort them by the absolute difference between the two dates, then take the first.

UserObj user = userObjList
    .Where(u => u.Name == inputName)
    .OrderBy(u => Math.Abs((u.Date - inputDT).TotalSeconds))
    .First();

or using the MinBy extension:

UserObj user = userObjList
    .Where(u => u.Name == inputName)
    .MinBy(u => Math.Abs((u.Date - inputDT).TotalSeconds));
Tim Rogers
  • 21,297
  • 6
  • 52
  • 68
  • I thought of this but its not optimal as it orders rather than simply selects the minimum. – gwizardry May 16 '12 at 13:55
  • 1
    @gwizardry: how big are your datasets? – Chris May 16 '12 at 14:05
  • Probably not very large ~100 but the operation may be repeated frequently and optimisation is important in general. – gwizardry May 16 '12 at 14:16
  • @gwizardry Then you can try an extension [like this](http://code.google.com/p/morelinq/source/browse/trunk/MoreLinq/MinBy.cs?r=83) that finds an element with a minimum value. Be sure to run comparison tests though if speed is crucial; you may find there's not a lot in it. – Tim Rogers May 16 '12 at 14:20
1

I think the following should work though it is untested...

var validUserObjs = userObjList.Where(u => u.Name ==inputName);
double mintime = validUserObjs.Min(u=>Math.Abs((u.Date-inputDate).TotalSeconds));
var result = validUserObjs.First(u=>Math.Abs((u.Date-inputDate).TotalSeconds)==mintime);

The idea is that it gets the valid userobjs by user first. Then it finds how far the closest date is from the input date. Then it finds the specific item by using that time difference.

Its a lot messier than that given one with order by but it should be O(N) to do it (one pass through the list to find the minimum and then a second pass to get that item). It may need some minor tweaking for errors in my code but the idea is sound. :)

Chris
  • 27,210
  • 6
  • 71
  • 92