【问题标题】:Drop the last item with LINQ [duplicate]使用 LINQ 删除最后一项 [重复]
【发布时间】:2010-11-12 16:01:12
【问题描述】:

使用 LINQ 似乎是一项微不足道的任务(可能确实如此),但我无法弄清楚如何使用 LINQ 删除序列的最后一项。使用 Take 并传递序列的长度 - 1 当然可以。但是,当在一行代码中链接多个 LINQ 时,这种方法似乎很不方便。

IEnumerable<T> someList ....

// this works fine
var result = someList.Take(someList.Count() - 1);


// but what if I'm chaining LINQ ?
var result = someList.Where(...).DropLast().Select(...)......;

// Will I have to break this up?
var temp = someList.Where(...);
var result = temp.Take(temp.Count() - 1).Select(...)........;

在 Python 中,我可以只做 seq[0:-1]。我尝试将 -1 传递给 Take 方法,但它似乎没有做我需要的。

【问题讨论】:

    标签: c# linq


    【解决方案1】:

    对于 .NET Core 2+ 和 .NET Standard 2.1 (planned)您可以使用 .SkipLast(1)

    对于其他平台,您可以编写自己的 LINQ 查询运算符(即,IEnumerable&lt;T&gt; 上的 extension method),例如:

    static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source)
    {
        using (var e = source.GetEnumerator())
        {
            if (e.MoveNext())
            {
                for (var value = e.Current; e.MoveNext(); value = e.Current)
                {
                    yield return value;
                }
            }
        }
    }
    

    xs.Take(xs.Count() - 1)等其他方法不同,上述方法只处理一次序列。

    【讨论】:

    • 很遗憾这个答案没有出现在flagged dupe 中。它比那里接受的答案更整洁。
    • 如何适应这个 - 很好 - 以便跳过最后的 n 个项目?
    • @Gerard:你没有。此答案中的解决方案针对 n = 1 的情况进行了优化。更一般的情况将涉及缓冲。 IIRC 有一个答案,如果您遇到重复的问题,则可以为更一般的情况提供解决方案。
    【解决方案2】:
    someList.Reverse().Skip(1).Reverse()
    

    【讨论】:

    • @LukeH,这是 O(n),这是 OP 想要的单行答案。
    • 哦,我为什么没想到这一点 :) 这是一种聪明而简单的方法,可以做开箱即用的东西。不太确定性能影响是什么,但如果我只需要一种快速而肮脏的方法来完成它,这是一个不错的方法。
    • @Saeed:它是 O(n),但它需要相当于四次通过序列。 Jamiec 的答案中的第一个示例也是单线和 O(n),但只需要两次通过序列(如果序列实现 ICollection&lt;T&gt;ICollection,则只需一次通过。)
    • 请注意,这也会将整个序列存储在内存中 - 两次。如果序列非常长,那么这可能是 lot 的内存。是的,这种技术在时间上是 O(n),但在空间上也是 O(n),在空间上可能是 O(1)。
    • @Saeed:为什么 IEnumerable 必须采用 O(n)?例如,Enumerable.Range 在空间中为 O(1),无论该范围内有多少项。
    【解决方案3】:

    您可以使用扩展方法轻松完成此操作,可以使用您已经找到的表单,也可以使用TakeCount 的组合

    public static IEnumerable<T> DropLast<T>(this IEnumerable<T> enumerable)
    {
      return enumerable.Take(enumerable.Count()-1);
    }
    

    【讨论】:

    • 第二种方式编译不成功吗? Last() 返回 T,但 except 采用 IEnumerable。我错过了什么吗?
    • @Kei - 可能是对的,我没有测试它,但类似的东西会起作用。编辑答案
    • Except 方法不适用于此,因为它还会从源序列中删除任何重复项。如果源是{1,2,3,4,5,4,3,2,1},那么OP 会期望结果是{1,2,3,4,5,4,3,2};使用您的第二个示例(假设您修复了错误)将给出{2,3,4,5}
    • 这也有效:xs.TakeWhile((x, i) => i
    • 哎呀,@RazMegrelidze,不要那样做。对于IEnumerable&lt;&gt;n 元素,此答案中的代码最终将枚举2n - 1 总元素:一个完整​​的枚举来计算Count(),然后是不带nth 元素的部分枚举。调用 Count() for each (TakeWhile()) n 元素,就像你一样,最终将枚举 n² + n 总元素;对于枚举超过 一百万 个元素的 1000-element 序列。 ,每个人,仅仅因为代码有效(或看起来有效),在点击△或复制粘贴之前真正考虑它在做什么
    猜你喜欢
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多