linq扩展方法从序列的末尾获取元素

时间:2010-09-23 17:07:00

标签: c# .net linq ienumerable linq-extensions

有可枚举的扩展方法

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

从头开始接受第一个count元素。

有没有办法从最后获取元素? 或者更好的方法是将元素从偏移量带到最后?

由于

4 个答案:

答案 0 :(得分:13)

finiteList.Reverse().Take(count).Reverse();

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

这样做有一些开销,所以自定义方法会更好。

更新:自定义方法

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;
    }
}

更新:根据dtb的答案更改了代码: - )

对熊的评论:请看这个例子:

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...

您可能会更改lastFive2的值,因此该方法可能不安全或至少不是功能方式。

承担:

我对安全的意义是:

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?

在这些情况下,您必须制作副本才能确定。但在大多数情况下,你的方式会很好 - 比这更有效,所以+1:)

一个想法是使用只有内部Enqueue等的队列。

答案 1 :(得分:3)

MoreLINQ提供TakeLast extension method

var last10 = finiteList.TakeLast(10);

要将元素从偏移量转移到结尾,Enumerable.Skip应该可以解决问题:

var allFromOffsetToEnd = finiteList.Skip(offset);

答案 2 :(得分:2)

@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(); 
} 

答案 3 :(得分:1)

关于表现的说明。这里有很多答案在IEnumerable<>运行,这可能就是你需要和应该使用的。

但是如果数据集很大并且类型为List<>或类似,那么您可以使用以下内容阻止大量不必要的迭代:

// demo, no errorhandling
public static IEnumerable<T> TakeFrom<T>(this IList<T> list, int offset)
{
    for (int i = offset; i < list.Count; i += 1)
    {
        yield return list[i];
    }
}