Remove 3 oldest elements from a List<> in C#
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 old开发者_如何学编程est elements?
stuff.RemoveAll(item => ???)
If you only need to enumerate the items, this will work:
stuff.OrderBy(item => item.Date).Skip(3);
If you actually want it in list form you will have to call .ToList()
afterwards:
stuff = stuff.OrderBy(item => item.Date).Skip(3).ToList();
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);
If your list is ordered you could simply use the RemoveRange
method:
int n = 3;
stuff.RemoveRange(stuff.Count - n, n);
const int cToRemove = 3;
var top3 = (from c in stuff
orderby c.Date ascending
select c).Take(cToRemove);
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 :)
精彩评论