tags:

views:

279

answers:

5

Let's say I have an object:

public class CustomObj
{
    DateTime Date { get; set; }
    String Name { get; set; }
}

Then let's say I have a List with 20 various elements.

var stuff = new List<CustomObj>
{
    { Date = DateTime.Now, Name = "Joe" },
    { Date = DateTime.Now.AddDays(1), Name = "Joe2" },
    { Date = DateTime.Now.AddDays(2), Name = "Joe3" },
    { Date = DateTime.Now.AddDays(3), Name = "Joe4" },
    { Date = DateTime.Now.AddDays(4), Name = "Joe5" },
    { Date = DateTime.Now.AddDays(5), Name = "Joe6" },
    { Date = DateTime.Now.AddDays(6), Name = "Joe7" },
    { Date = DateTime.Now.AddDays(7), Name = "Joe8" },
    { Date = DateTime.Now.AddDays(8), Name = "Joe9" },
    { Date = DateTime.Now.AddDays(9), Name = "Joe10" },
    { Date = DateTime.Now.AddDays(10), Name = "Joe11" }
}

How can I remove the 3 oldest elements?

stuff.RemoveAll(item => ???)
+3  A: 

If your list is ordered you could simply use the RemoveRange method:

int n = 3;
stuff.RemoveRange(stuff.Count - n, n);
CMS
it should be ordered first, then we remove last 3
Anwar Chandra
+7  A: 

This should work:

stuff.OrderBy(item => item.Date).Skip(3);

Edit:

Ahh yes, this is what happens when you have to get up in 2.5 hrs for a flight to San Francisco but you can't fall asleep...

If you want it in list form you will have to call .ToList() afterwards:

stuff = stuff.OrderBy(item => item.Date).Skip(3).ToList();

If anyone else is attending the Business of Software conference tomorrow I'll be at the nearest coffee shop :)

John Rasch
it does not remove the items, just skips them and does what with the return value?
Johannes Rudolph
He probably wants to do a .ToList() after it.
Dykam
It doesn't remove the items from the source list, but the OP could assign the return value to something else, e.g. call ToList and then reassign to the `stuff` variable.
Jon Skeet
True. It ain't the most efficient method, but it is the cleanest.
Dykam
+1  A: 
const int cToRemove = 3;

var top3 = (from c in stuff
        orderby c.Date ascending
        select c).Take(cToRemove);
Muad'Dib
+4  A: 

If you're willing to replace the list with a new one, you could try this:

stuff = stuff.OrderBy( c => c.Date).Skip(3).ToList();

On the other hand, if you need stuff to remain the same exact List<T> instance, you could sort it and then remove a range by index:

stuff.Sort(...);
stuff.RemoveRange(0, 3);
Bevan
+1  A: 

All the other answers so far have relied on sorting the list, which is an O(n log n) operation if you don't already have it sorted.

Here's a solution which is O(n) albeit it with a horrible constant factor. It uses MinBy from MoreLINQ - you could easily rewrite that in your own code if you need to, and even make it return the index directly instead of the value (and useRemoveAt instead of Remove).

// The list.Count part is in case the list starts off with
// fewer than 3 elements
for (int i = 0; i < 3 && list.Count > 0; i++)
{
    var oldest = list.MinBy(x => x.Date);
    list.Remove(oldest);
}

You could certainly write this more efficiently to find the oldest three elements in a single pass of the list - but the code would be significantly more complicated, leading to more chances for errors. The above should work fine in O(n), even if it's lacking in elegance when you think of it going through the list 6 times :)

Jon Skeet