tags:

views:

91

answers:

4

I have as List of strings with where i remove each duplicates, now I want to filter it even more to get the last 5 records. How can I do this?

What I got so far

 List<string> query = otherlist.Distinct().Select(a => a).ToList();
+3  A: 

How about this?

var lastFive = list.Reverse().Take(5).Reverse();

edit: here's the whole thing -

var lastFiveDistinct = otherlist.Distinct()
                                .Reverse()
                                .Take(5)
                                .Reverse()
                                .ToList();

Also note that you shouldn't call it query if you've got a ToList() call at the end, because then it's not a query anymore, it's been evaluated and turned into a list. If you only need it to iterate over, you can omit the ToList() call and leave it as an IEnumerable.

tzaman
I'd venture that it would be cheaper to count the elements then use Skip and Take. Reversing an IEnumerable can't be cheap.
spender
It's the same asymptotic complexity either way, but you're right that skip/take is probably faster.
tzaman
Actually, I'll amend my earlier statement - I don't think there will be a noticeable performance difference. See my comment on Jens' answer.
tzaman
+4  A: 

You do not need the .Select(a => a). Thats redundant.

You can get the last 5 records, by skipping over the rest like

List<string> query = otherlist.Distinct().ToList();
List<string> lastFive = query.Skip(query.Count-5).ToList();
Jens
Skip() has a better performance than Reverse() then Take(), obviously.
Danny Chen
@Danny: Not as much as you'd think: the `ToList()` call will be just as expensive as my first `Reverse()` (traverse the whole list once), and after the `Take(5)` it's just reversing five elements which is peanuts. Also, my way doesn't create an intermediate list.
tzaman
@tzaman - " my way doesn't create an intermediate list" - yes it does; two of them, in fact. How do you think `Reverse` operates? (well, strictly speaking it creates an `T[]` array, courtesy of `Buffer<T>` - but same difference)
Marc Gravell
@Marc - huh. Of course; silly of me. Still, a bit of timing I just did now seems to have indicate my solution running faster than this one (or yours). Take a look? http://pastebin.com/a65UMZfP
tzaman
A: 
var count=list.Count();
var last5=list.Skip(count-5);

EDIT:

I missed that the data is List<T> . This approach would be better for IEnumerable<T>

spender
+3  A: 

edit to cater for non-list inputs, now handles IEnumerable<T> and checks if this is an IList<T>; if not it buffers it via ToList(), which helps ensure we only read the data once (rather than .Count() and .Skip() which may read the data multiple times).

Since this is a list, I'd be inclined to write an extension method that uses that to the full:

    public static IEnumerable<T> TakeLast<T>(
           this IEnumerable<T> source, int count)
    {
        IList<T> list = (source as IList<T>) ?? source.ToList();
        count = Math.Min(count, list.Count);
        for (int i = list.Count - count; i < list.Count; i++)
        {
            yield return list[i];
        }
    }
Marc Gravell
Absolutely. Given you know it's a list, this is a simple and easily the most efficient approach.
Noldorin
Except it won't be a list when it comes out of the `Distinct()` call; the `ToList()` only needs to happen once at the end.
tzaman
@tzaman - fine; will edit
Marc Gravell