8

列挙可能な拡張メソッドがあります

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

これは最初countから最初の要素を取ります。

要素を最後から取得する方法はありますか?または、オフセットから最後まで要素を取得するためのさらに良い方法はありますか?

ありがとう

4

4 に答える 4

15
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 :)

アイデアは、内部エンキューなどのみを持つキューを使用することです。

于 2010-09-23T17:09:04.693 に答える
3

MoreLINQTakeLast拡張メソッドを提供します:

var last10 = finiteList.TakeLast(10);

要素をオフセットから最後まで取得するには、Enumerable.Skipで次のトリックを実行する必要があります。

var allFromOffsetToEnd = finiteList.Skip(offset);
于 2010-09-23T17:18:12.583 に答える
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(); 
} 
于 2010-09-23T18:57:42.083 に答える
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];
    }
}
于 2010-09-23T17:57:42.403 に答える