Taufiq Abdur Rahman
Taufiq Abdur Rahman

Reputation: 1388

Selecting x number of elements from a System.Collections.Generic.List c#

What i need is a way to select the last 100 elements from a list, as list

    public List<Model.PIP> GetPIPList()
    {
        if (Repository.PIPRepository.PIPList == null)
            Repository.PIPRepository.Load();
        return Repository.PIPRepository.PIPList.Take(100);

    }

I get error like this

'System.Collections.Generic.IEnumerable' to 'System.Collections.Generic.List'. An explicit conversion exists (are you missing a cast?)

Upvotes: 4

Views: 5989

Answers (5)

phoog
phoog

Reputation: 43046

If your list is large, you'll get the best performance by rolling your own:

public static class ListExtensions
{
    public static IEnumerable<T> LastItems<T>(this IList<T> list, int numberOfItems) //Can also handle arrays
    {
        for (int index = Math.Max(list.Count - numberOfItems, 0); index < list.Count; index++)
            yield return list[index];
    }
}

Why is this faster than using Skip()? If you have a list with 50,000 items, Skip() calls MoveNext() on the enumerator 49,900 times before it starts returning items.

Why is it faster than using Reverse()? Because Reverse allocates a new array large enough to hold the list's elements, and copies them into the array. This is especially good to avoid if the array is large enough to go on the large object heap.

Upvotes: 5

leppie
leppie

Reputation: 117220

somelist.Reverse().Take(100).Reverse().ToList();

This would be much cheaper than ordering :) Also preserves the original ordering.

Upvotes: 11

Andrew Barber
Andrew Barber

Reputation: 40150

EDIT: I missed that you said you wanted the last 100 items, and weren't able to do that yet.

To get the last 100 items:

return Repository.PIPRepository.PIPList
    .OrderByDescending(pip=>pip.??).Take(100)
    .OrderBy(pip=>pip.??);

...and then change your method signature to return IEnumerable<Model.PIP>

?? signifies what ever property you would be sorting on.

Joel also gives a great solution, based on counting the number of items in the last, and skipping all but 100 of them. In many cases, that probably works better. I didn't want to post the same solution in my edit! :)

Upvotes: 4

Joel Coehoorn
Joel Coehoorn

Reputation: 415690

The .Take() method returns and IEnumerable<T> rather than a List<T>. This is a good thing, and you should strongly consider altering your method and your work habits to use IEnumerable<T> rather than List<T> as much as is practical.

Aside from that, .Take(100) will also return the first 100 elements, rather than the last 100 elements. You want something like this:

public IEnumerable<Model.PIP> GetPIPs()
{
    if (Repository.PIPRepository.PIPList == null)
        Repository.PIPRepository.Load();
    return Repository.PIPRepository.PIPList.Skip(Math.Max(0,Repository.PIPRepository.PIPList.Count - 100));
}

If you really need a list rather than an enumerable (hint: you probably don't), it's still better to build this method using an IEnumerable and use .ToList() at the place where you call this method.

At some point in the future you'll want to go back and update your Load() code to also use IEnumerable, as well as code later on in the process. The ultimate goal here is to get to the point where you are effectively streaming your objects to the browser, and only ever one have one of them loaded into memory on your web server at a time. IEnumerable allows for this. List does not.

Upvotes: 2

ysrb
ysrb

Reputation: 6740

Try:

public List<Model.PIP> GetPIPList()
    {
        if (Repository.PIPRepository.PIPList == null)
            Repository.PIPRepository.Load();
        return Repository.PIPRepository.PIPList.Take(100).ToList();

    }

Upvotes: 2

Related Questions