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

发布于 2024-09-24 12:36:31 字数 217 浏览 3 评论 0原文

有一个可枚举的扩展方法

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

,它从一开始就获取第一个 count 元素。

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

谢谢

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

岁月染过的梦 2024-10-01 12:36:31
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 答案的想法稍微更改了代码:-)

对 Bear 的评论: 看看这个示例:

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 等的队列。

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.

一曲琵琶半遮面シ 2024-10-01 12:36:31

MoreLINQ 提供了TakeLast 扩展方法

var last10 = finiteList.TakeLast(10);

要从偏移量到末尾获取元素,Enumerable.Skip 应该可以解决问题:

var allFromOffsetToEnd = finiteList.Skip(offset);

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);
路弥 2024-10-01 12:36:31

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

@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(); 
} 
疯狂的代价 2024-10-01 12:36:31

关于性能的说明。这里有很多关于 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];
    }
}

A note on performance. Plenty of answers here operating on IEnumerable<> and that is probably what you need and should use.

But if the datasets are large and of type List<> or similar, you can prevent a lot of unnecessary iterating with something like:

// 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];
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文