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

前端 未结 22 1613
南笙
南笙 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:15

    The solution that I use for this problem is slightly more elaborate.

    My util static class contains an extension method MarkEnd which converts the T-items in EndMarkedItem-items. Each element is marked with an extra int, which is either 0; or (in case one is particularly interested in the last 3 items) -3, -2, or -1 for the last 3 items.

    This could be useful on its own, e.g. when you want to create a list in a simple foreach-loop with commas after each element except the last 2, with the second-to-last item followed by a conjunction word (such as “and” or “or”), and the last element followed by a point.

    For generating the entire list without the last n items, the extension method ButLast simply iterates over the EndMarkedItems while EndMark == 0.

    If you don’t specify tailLength, only the last item is marked (in MarkEnd()) or dropped (in ButLast()).

    Like the other solutions, this works by buffering.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace Adhemar.Util.Linq {
    
        public struct EndMarkedItem {
            public T Item { get; private set; }
            public int EndMark { get; private set; }
    
            public EndMarkedItem(T item, int endMark) : this() {
                Item = item;
                EndMark = endMark;
            }
        }
    
        public static class TailEnumerables {
    
            public static IEnumerable ButLast(this IEnumerable ts) {
                return ts.ButLast(1);
            }
    
            public static IEnumerable ButLast(this IEnumerable ts, int tailLength) {
                return ts.MarkEnd(tailLength).TakeWhile(te => te.EndMark == 0).Select(te => te.Item);
            }
    
            public static IEnumerable> MarkEnd(this IEnumerable ts) {
                return ts.MarkEnd(1);
            }
    
            public static IEnumerable> MarkEnd(this IEnumerable ts, int tailLength) {
                if (tailLength < 0) {
                    throw new ArgumentOutOfRangeException("tailLength");
                }
                else if (tailLength == 0) {
                    foreach (var t in ts) {
                        yield return new EndMarkedItem(t, 0);
                    }
                }
                else {
                    var buffer = new T[tailLength];
                    var index = -buffer.Length;
                    foreach (var t in ts) {
                        if (index < 0) {
                            buffer[buffer.Length + index] = t;
                            index++;
                        }
                        else {
                            yield return new EndMarkedItem(buffer[index], 0);
                            buffer[index] = t;
                            index++;
                            if (index == buffer.Length) {
                                index = 0;
                            }
                        }
                    }
                    if (index >= 0) {
                        for (var i = index; i < buffer.Length; i++) {
                            yield return new EndMarkedItem(buffer[i], i - buffer.Length - index);
                        }
                        for (var j = 0; j < index; j++) {
                            yield return new EndMarkedItem(buffer[j], j - index);
                        }
                    }
                    else {
                        for (var k = 0; k < buffer.Length + index; k++) {
                            yield return new EndMarkedItem(buffer[k], k - buffer.Length - index);
                        }
                    }
                }    
            }
        }
    }
    

提交回复
热议问题