views:

93

answers:

3

Hi

There is the enumerable extension method

Take<TSource>(
    IEnumerable<TSource> source,
    int count
)

which takes the first count elements from the start.

Is there a way to take the elements from the end? or even better a way to take the elements from an offset to the end?

Thanks

+7  A: 
finiteList.Reverse().Take(count).Reverse();

or

finiteList.Skip(finiteList.Count() - count)

There is some overhead in doing this so a custom method would be better.

Update: A custom method

public static class EnumerableExtensions
{
    public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int count)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (count < 0) throw new ArgumentOutOfRangeException("count");

        if (count == 0) yield break;

        var queue = new Queue<T>(count);

        foreach (var t in source)
        {
            if (queue.Count == count) queue.Dequeue();

            queue.Enqueue(t);
        }

        foreach (var t in queue)
            yield return t;
    }
}

Update: Changed the code a littlebit with ideas from dtb´s answer :-)

Comment to Bear: Look at this example:

var lastFive = Enumerable.Range(1, 10).TakeLast(5);
var lastFive2 = Enumerable.Range(1, 10).TakeLast2(5); //Bear´s way

Queue<int> q = (Queue<int>)lastFive2;
q.Dequeue();

//Is lastFive2 still last five? no...

You could potentially change the values of lastFive2 and therefore that approach can be unsafe or at least it´s not the functional way.

To Bear:

What I meant about safe is this:

var lastFive2 = Enumerable.Range(1, 10).TakeLast2(5); //Bear´s way

//some = Some method which you don't control - it could be from another assembly which represents a crazy plugin etc.
some(lastFive2);
//Now what?

In these cases you would have to make a copy to be sure. But in most cases your way would be fine - and a little bit more efficient than this so +1 :)

An idea is to use a queue which only have internal Enqueue etc.

lasseespeholt
Yikes, that'll work, but it's not gonna be efficient, nor will it be elegant.
Mark
@Mark As I state, I agree with you :) I will come up with a more efficient method...
lasseespeholt
Also, depending on what `finiteList` is, it may or may not even be POSSIBLE to enumerate it twice (which your second suggestion would require)
Mark
second one is fine, but first is uuugly
Andrey
arg. didn't see the skip method
Fabiano
TakeLast doesnt need to be an iterator as its aggregating. The redundant yields can be replaced with return queue.
Bear Monkey
@Bear ahh yes! Didn't even see that :D thanks for your contribution :)
lasseespeholt
@Bear Hhm no, not quite. A `(Queue)some.TakeLast(5)` could give unexpected results... but generally, it would be fine :)
lasseespeholt
@lasse Not sure I follow? See post below for what i meant.
Bear Monkey
@Bear Yes, I know what you meant, but I have updated with an example of what I mean :)
lasseespeholt
@lasse ok i see what you mean but it is safe because your not mutating the source collection. Nonetheless your right it probably not functional though.
Bear Monkey
@lasse ok i concede, i think your approach is better than mine ;) Its shame ReadOnlyCollection doesnt take an ICollection then we could just wrap queue in that and we both win!
Bear Monkey
@Bear I like your solution - I would use it when skipping a enumerator would be important (properly not very often, though). ReadOnlyCollection would also wrap the IEnumerable so it is properly implemented like my loop and therefore the efficiency is gone.
lasseespeholt
@lasse Ok good point about ReadOnlyCollection but what about ReadOnlyEnumerable? Your thought would be appreciated. Ive added the class to my post I made to my earlier. I could make this a new question if you think its interesting enough.
Bear Monkey
+3  A: 

MoreLINQ provides a TakeLast extension method:

var last10 = finiteList.TakeLast(10);

To take the elements from an offset to the end, Enumerable.Skip should do the trick:

var allFromOffsetToEnd = finiteList.Skip(offset);
dtb
+1 ALMOST exactly what I came up with - a little later than your post ;)
lasseespeholt
+2  A: 

@lasseespeholt:

public static class EnumerableExtensions
{
    public static ReadOnlyEnumerable<T> AsReadOnly<T>(
         this IEnumerable<T> source)
    {
        return new ReadOnlyEnumerable<T>(source);
    }
}

public sealed class ReadOnlyEnumerable<T> : IEnumerable<T>
{
    private readonly IEnumerable<T> _source;

    public ReadOnlyEnumerable(IEnumerable<T> source)
    {
        if (_source == null)
        {
            throw new ArgumentNullException("source");
        }

        _source = source;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return _source.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return _source.GetEnumerator();
    }
}

public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int count) 
{ 
    if (source == null) throw new ArgumentNullException("source"); 
    if (count < 0) throw new ArgumentOutOfRangeException("count"); 

    if (count == 0) 
       return Enumerable.Empty<T>();

    var queue = new Queue<T>(count); 

    foreach (var t in source) 
    { 
        if (queue.Count == count) queue.Dequeue(); 

        queue.Enqueue(t); 
    } 

    return queue.AsReadOnly(); 
} 
Bear Monkey
+1 For efficiency... but look at my updated answer
lasseespeholt
Ta, thank you very much :)
Bear Monkey
What exactly is `ReadOnlyEnumerable<T>` there for? The `IEnumerable`/`IEnumerable<T>` interfaces are already read-only.
LukeH
Please see the accepted answer. I've added this is a reply to a discussion with lasseespeholt. Basically its to guard against modification by consumers who could upcast the IEnumerable<T> to a Queue<T> and modify it. I was hoping to get his opinion.
Bear Monkey