You can create something like what you want, but only in very limited circumstances:
public static class IEnumerableExt {
public static IEnumerable<T> ToIEnumerable<T>(this IEnumerator<T> e) {
while (e.MoveNext())
yield return e.Current;
}
public static (IEnumerable<T> first, IEnumerable<T> rest) FirstRest<T>(this IEnumerable<T> src, Func<T,bool> InFirstFn) {
var e = src.GetEnumerator();
var first = new List<T>();
while (e.MoveNext() && InFirstFn(e.Current))
first.Add(e.Current);
return (first, e.ToIEnumerable());
}
}
Note that this has to iterate over and buffer first
before it can return (what if you tried to enumerate rest
before first
?) and you can't call Reset
on rest
and expect anything reasonable. Fixing these issues would involve a lot more code.
I can dimly see in the distance some type of extended LINQ where you pass Action
s and Func
s and do something like continuations (the rest of the IEnumerable
) to process, but I am not sure it is worth it. Something like:
public static IEnumerable<T> DoWhile<T>(this IEnumerable<T> src, Func<T,bool> whileFn, Action<T> doFn) {
var e = src.GetEnumerator();
while (e.MoveNext() && whileFn(e.Current))
doFn(e.Current);
return e.ToIEnumerable();
}
while you could use like:
while (data.Any()) {
var y = data.First().Year;
var ct = 0;
data = data.DoWhile(d => d.Year == y, d => ++ct);
Console.WriteLine($"{ct} items in {y}");
}
The best answer is to stop using the IEnumerable<T>
automatic enumeration and manually enumerate:
for (var e = data.GetEnumerator(); e.MoveNext();) {
var y = e.Current.Year;
var ct = 0;
while (e.Current.Year == y)
++ct;
Console.WriteLine($"{ct} items in {y}");
}
Once you are doing manual enumeration, you can handle most any circumstance without losing efficiency to buffering, or delegate calls for your specific needs.
PS: Note that testing data.Count()
against 0
is very inefficient, you should always be using data.Any()
. Depending on data
, data.Count()
may never return, or may be very expensive however even data.Any()
may lose data.First()
.
PPS: A more efficient version of ToIEnumerable
would return a custom class that just returns the IEnumerator
to GetEnumerator
but would have all the caveats and possibly more. The sample ToEnumerable
creates daisy chains of while
loops.