简体   繁体   中英

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 oldest 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 use RemoveAt 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 :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM