假设我有一个序列.
IEnumerable<int> sequence = GetSequenceFromExpensiveSource();
// sequence now contains: 0,1,2,3,...,999999,1000000
Run Code Online (Sandbox Code Playgroud)
获取序列并不便宜并且是动态生成的,我想只迭代一次.
我想得到0 - 999999(即除了最后一个元素之外的所有东西)
我知道我可以这样做:
sequence.Take(sequence.Count() - 1);
Run Code Online (Sandbox Code Playgroud)
但是这导致了两个大序列的枚举.
是否有LINQ结构可以让我这样做:
sequence.TakeAllButTheLastElement();
Run Code Online (Sandbox Code Playgroud)
Dar*_*rio 57
我不知道Linq解决方案 - 但您可以使用生成器(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()));
}
Run Code Online (Sandbox Code Playgroud)
或者作为丢弃最后n个项目的一般化解决方案(使用评论中建议的队列):
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()));
}
Run Code Online (Sandbox Code Playgroud)
Kam*_*rey 43
作为创建自己的方法的替代方法,在元素顺序不重要的情况下,下一个将起作用:
var result = sequence.Reverse().Skip(1);
Run Code Online (Sandbox Code Playgroud)
Jor*_*ren 42
因为我不是明确使用a的粉丝Enumerator,所以这是另一种选择.请注意,需要使用包装器方法让无效参数尽早抛出,而不是推迟检查,直到实际枚举序列为止.
public static IEnumerable<T> DropLast<T>(this IEnumerable<T> source)
{
if (source == null)
throw new ArgumentNullException("source");
return InternalDropLast(source);
}
private static IEnumerable<T> InternalDropLast<T>(IEnumerable<T> source)
{
T buffer = default(T);
bool buffered = false;
foreach (T x in source)
{
if (buffered)
yield return buffer;
buffer = x;
buffered = true;
}
}
Run Code Online (Sandbox Code Playgroud)
根据Eric Lippert的建议,它很容易推广到n个项目:
public static IEnumerable<T> DropLast<T>(this IEnumerable<T> source, int n)
{
if (source == null)
throw new ArgumentNullException("source");
if (n < 0)
throw new ArgumentOutOfRangeException("n",
"Argument n should be non-negative.");
return InternalDropLast(source, n);
}
private static IEnumerable<T> InternalDropLast<T>(IEnumerable<T> source, int n)
{
Queue<T> buffer = new Queue<T>(n + 1);
foreach (T x in source)
{
buffer.Enqueue(x);
if (buffer.Count == n + 1)
yield return buffer.Dequeue();
}
}
Run Code Online (Sandbox Code Playgroud)
我现在在屈服之前缓冲而不是屈服之后,所以这种n == 0情况不需要特殊处理.
Jus*_*ard 13
对于使用较新版本的.net的用户,该Enumerable.SkipLast(IEnumerable<TSource>, Int32)方法已在.NET Core 2.0中添加。
var sequence = GetSequence();
var allExceptLast = sequence.SkipLast(1);
Run Code Online (Sandbox Code Playgroud)
来源:https : //docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.skiplast
Nol*_*rin 12
BCL中没有任何东西(或者我相信的更多),但您可以创建自己的扩展方法.
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果.NET Framework附带了这样的扩展方法,将会很有帮助.
public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source, int count)
{
var enumerator = source.GetEnumerator();
var queue = new Queue<T>(count + 1);
while (true)
{
if (!enumerator.MoveNext())
break;
queue.Enqueue(enumerator.Current);
if (queue.Count > count)
yield return queue.Dequeue();
}
}
Run Code Online (Sandbox Code Playgroud)
在 C# 8.0 中,您可以为此使用范围和索引。
var allButLast = sequence[..^1];
Run Code Online (Sandbox Code Playgroud)
默认情况下,C# 8.0 需要 .NET Core 3.0 或 .NET Standard 2.1(或更高版本)。检查此线程以用于较旧的实现。
| 归档时间: |
|
| 查看次数: |
43798 次 |
| 最近记录: |