tags:

views:

223

answers:

3

Hi.

I have a small problem where I want to find the next "active" item in a list with linq. Then next "active" item is defined by a startDate and EndDate. Here is an example list.

    //-- Create Lists of turns    
    IList<Turn> turns= new List<Turn>(){ 
                new Turn(){Name = "Turn 1", StartDate = DateTime.Parse("2009-05-01"), EndDate = DateTime.Parse("2009-05-01") }
         ,      new Turn(){Name = "Turn 2", StartDate = DateTime.Parse("2009-06-01"), EndDate = DateTime.Parse("2009-06-01") }
         ,      new Turn(){Name = "Turn 3", StartDate = DateTime.Parse("2009-07-01"), EndDate = DateTime.Parse("2009-07-02") }
         ,      new Turn(){Name = "Turn 4", StartDate = DateTime.Parse("2009-08-01"), EndDate = DateTime.Parse("2009-08-03") }
    }

//-- Get the next Turn by DateTime.
DateTime toDay = DateTime.Parse("2009-06-02");

//-- Should return the "Turn 3" item...
Turn turn = (from item in turns
            where .....
            select turn).FirstOrDefault<Turn>();

Is there a good solution to find the next turn by using startDate/endDate properties on Turn. I have tryed to first order the list by startdate and the find First one in the list, but I wounder if there is a more "safe" way to get it that dosen't need the list in correct order to find the correct Turn.

A: 

I think you just want:

var turn = turns.SkipWhile(t => t.EndDate < today).FirstOrDefault();

This would return the first turn either containing or after the given date today, which what you seem to be looking for. It also assumes that your turns list is already sorted, of course.

Edit: It appears I missed the bit about you not wanting to sort the list. I'm still not completely sure on the conditions you require to select the appropiate turn, which would enable me to improve the query.

Noldorin
This might fail if the list is unsorted - you just return the first item after today, but not necessarily the first one after today.
Daniel Brückner
@Daniel: Yes, clearly. But the example in the question shows that his list is already sorted, so I was responding to that really. He mentioned sorting it anyway, so he's obviously aware of the issue. I'll add in the caveat nonetheless.
Noldorin
You made the same mistake as I did - missed the last lines of the question. He is looking for a way to get the next item but without sorting the list first because he seems not to trust in the sorting algorithm.
Daniel Brückner
Yeah, it seems I did. I'll edit the post.
Noldorin
A: 

Why don't you just get the first item with a start date after today? I added an explict OrderBy() call just to be sure that the list is sorted. If you know that it is sorted, you can leave that out, of course.

turns.OrderBy(t => t.StartDate).FirstOrDefault(t => t.StartDate > today);

UPDATE

I missed your last lines. Yes, you can do it without sorting the list explicitly. You have to search the list for the item with a start date after today and ther must be no item with a start date after today but before that of the current item. But this will really slow down the search because you have to look at the whole list for every item making the thing O(n²).

turns.Single(t => 
    t.StartDate > today &&
    turns.All(u => u.StartDate <= today || u.StartDate > t.StartDate))

This assumes that the turns are non-overlapping. If they overlapp, you must use First instead of Single or add additional constraints to get a unique result. And finally: use the the sorting solution - this solution is by no means safer than sorting and getting the first item.

Daniel Brückner
I tried the last statement but it was very slow, I guess I need to sort the list first and the get the FirstOrDefault...Looks like it's the simplest way to acive what I want...
Magnus Gladh
+1  A: 

You could do this.

Turn turn = turns
  .Where(t => t.StartDate >= toDay)
  .OrderBy(t => t.StartDate)
  .FirstOrDefault();

The Where call is optional - its goal is to reduce the amount of items that have to be ordered.

David B
If you remove the Where call, then it doesn't work actually. I think you mean that it serves two purposes - or at least it's more efficient to place it before the OrderBy call rather than after.
Noldorin
Oops, good point.
David B