How to take all but the last element in a sequence using LINQ?

前端 未结 22 1409
南笙
南笙 2020-11-30 02:51

Let\'s say I have a sequence.

IEnumerable sequence = GetSequenceFromExpensiveSource();
// sequence now contains: 0,1,2,3,...,999999,1000000


        
相关标签:
22条回答
  • 2020-11-30 03:10

    Nothing in the BCL (or MoreLinq I believe), but you could create your own extension method.

    public static IEnumerable<T> TakeAllButLast<T>(this IEnumerable<T> source)
    {
        using (var enumerator = source.GetEnumerator())
            bool first = true;
            T prev;
            while(enumerator.MoveNext())
            {
                if (!first)
                    yield return prev;
                first = false;
                prev = enumerator.Current;
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-30 03:10

    With C# 8.0 you can use Ranges and indices for that.

    var allButLast = sequence[..^1];
    

    By default C# 8.0 requires .NET Core 3.0 or .NET Standard 2.1 (or above). Check this thread to use with older implementations.

    0 讨论(0)
  • 2020-11-30 03:11

    I don't think it can get more succinct than this - also ensuring to Dispose the IEnumerator<T>:

    public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source)
    {
        using (var it = source.GetEnumerator())
        {
            if (it.MoveNext())
            {
                var item = it.Current;
                while (it.MoveNext())
                {
                    yield return item;
                    item = it.Current;
                }
            }
        }
    }
    

    Edit: technically identical to this answer.

    0 讨论(0)
  • 2020-11-30 03:12

    I don't know a Linq solution - But you can easily code the algorithm by yourself using generators (yield return).

    public static IEnumerable<T> TakeAllButLast<T>(this IEnumerable<T> source) {
        var it = source.GetEnumerator();
        bool hasRemainingItems = false;
        bool isFirst = true;
        T item = default(T);
    
        do {
            hasRemainingItems = it.MoveNext();
            if (hasRemainingItems) {
                if (!isFirst) yield return item;
                item = it.Current;
                isFirst = false;
            }
        } while (hasRemainingItems);
    }
    
    static void Main(string[] args) {
        var Seq = Enumerable.Range(1, 10);
    
        Console.WriteLine(string.Join(", ", Seq.Select(x => x.ToString()).ToArray()));
        Console.WriteLine(string.Join(", ", Seq.TakeAllButLast().Select(x => x.ToString()).ToArray()));
    }
    

    Or as a generalized solution discarding the last n items (using a queue like suggested in the comments):

    public static IEnumerable<T> SkipLastN<T>(this IEnumerable<T> source, int n) {
        var  it = source.GetEnumerator();
        bool hasRemainingItems = false;
        var  cache = new Queue<T>(n + 1);
    
        do {
            if (hasRemainingItems = it.MoveNext()) {
                cache.Enqueue(it.Current);
                if (cache.Count > n)
                    yield return cache.Dequeue();
            }
        } while (hasRemainingItems);
    }
    
    static void Main(string[] args) {
        var Seq = Enumerable.Range(1, 4);
    
        Console.WriteLine(string.Join(", ", Seq.Select(x => x.ToString()).ToArray()));
        Console.WriteLine(string.Join(", ", Seq.SkipLastN(3).Select(x => x.ToString()).ToArray()));
    }
    
    0 讨论(0)
  • 2020-11-30 03:12

    If speed is a requirement, this old school way should be the fastest, even though the code doesn't look as smooth as linq could make it.

    int[] newSequence = int[sequence.Length - 1];
    for (int x = 0; x < sequence.Length - 1; x++)
    {
        newSequence[x] = sequence[x];
    }
    

    This requires that the sequence is an array since it has a fixed length and indexed items.

    0 讨论(0)
  • 2020-11-30 03:13

    Why not just .ToList<type>() on the sequence, then call count and take like you did originally..but since it's been pulled into a list, it shouldnt do an expensive enumeration twice. Right?

    0 讨论(0)
提交回复
热议问题