如何采取一切,但在使用LINQ序列中的最后一个元素?但在、序列、元素、LINQ

2023-09-02 10:45:04 作者:提笔废江山

让我们说我有一个序列。

 的IEnumerable< INT>序列= GetSequenceFromExpensiveSource();
//序列现在包含:0,1,2,3,...,999999,1000000
 

获取顺序并不便宜,动态生成的,我想遍历一次而已。

我想获得0 - 999999(即一切,但最后一个元素)

我承认,我可以做这样的事情:

  sequence.Take(sequence.Count() -  1);
 
关于LINQ

但导致这两个枚举过大的顺序。

有一个LINQ构造,让我做的:

  sequence.TakeAllButTheLastElement();
 

解决方案

我不知道LINQ的解决方案 - 但你可以使用发电机(收益率回报)容易code算法自己。

 公共静态的IEnumerable< T> TakeAllButLast< T>(这IEnumerable的< T>源){
    VAR IT = source.GetEnumerator();
    布尔hasRemainingItems = FALSE;
    布尔isFirst = TRUE;
    牛逼项=默认(T);

    做 {
        hasRemainingItems = it.MoveNext();
        如果(hasRemainingItems){
            如果产量退货项目(isFirst!);
            项目= it.Current;
            isFirst = FALSE;
        }
    }而(hasRemainingItems);
}

静态无效的主要(字串[] args){
    变种SEQ = Enumerable.Range(1,10);

    Console.WriteLine(的string.join(,,Seq.Select(X => x.ToString())的ToArray())。);
    Console.WriteLine(的string.join(,,Seq.TakeAllButLast()选择(X => x.ToString())的ToArray())。);
}
 

或作为广义解丢弃的最后n项(使用队列就像在评论建议):

 公共静态的IEnumerable< T> SkipLastN< T>(这IEnumerable的< T>源,INT N){
    VAR IT = source.GetEnumerator();
    布尔hasRemainingItems = FALSE;
    VAR缓存=新的队列< T>(N + 1);

    做 {
        如果(hasRemainingItems = it.MoveNext()){
            cache.Enqueue(it.Current);
            如果(cache.Count将N)
                收益回报cache.Dequeue();
        }
    }而(hasRemainingItems);
}

静态无效的主要(字串[] args){
    变种SEQ = Enumerable.Range(1,4);

    Console.WriteLine(的string.join(,,Seq.Select(X => x.ToString())的ToArray())。);
    Console.WriteLine(的string.join(,,Seq.SkipLastN(3)。选择(X => x.ToString())的ToArray())。);
}
 

Let's say I have a sequence.

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

Getting the sequence is not cheap and is dynamically generated, and I want to iterate through it once only.

I want to get 0 - 999999 (i.e. everything but the last element)

I recognize that I could do something like:

sequence.Take(sequence.Count() - 1);

but that results in two enumerations over the big sequence.

Is there a LINQ construct that lets me do:

sequence.TakeAllButTheLastElement();

解决方案

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