Suppose I have an IEnumerable<T>
, and I want to take the first element and pass the remaining elements to some other code. I can get the first n elements using Take(n)
, but how can I then access the remaining elements without causing the enumeration to re-start?
For example, suppose I have a method ReadRecords
that accepts the records in a CSV file as IEnumerable<string>
. Now suppose that within that method, I want to read the first record (the headers), and then pass the remaining records to a ReadDataRecords
method that also takes IEnumerable<string>
. Like this:
void ReadCsv(IEnumerable<string> records)
{
var headerRecord = records.Take(1);
var dataRecords = ???
ReadDataRecords(dataRecords);
}
void ReadDataRecords(IEnumerable<string> records)
{
// ...
}
If I were prepared to re-start the enumeration, then I could use dataRecords = records.Skip(1)
. However, I don't want to re-start it - indeed, it might not be re-startable.
So, is there any way to take the first records, and then the remaining records (other than by reading all the values into a new collection and re-enumerating them)?
This is an interesting question, I think you can use a workaround like this, instead of using LINQ
get the enumerator and use it:
private void ReadCsv(IEnumerable<string> records)
{
var enumerator = records.GetEnumerator();
enumerator.MoveNext();
var headerRecord = enumerator.Current;
var dataRecords = GetRemainingRecords(enumerator);
}
public IEnumerable<string> GetRemainingRecords(IEnumerator<string> enumerator)
{
while (enumerator.MoveNext())
{
if (enumerator.Current != null)
yield return enumerator.Current;
}
}
Update: According to your comment here is more extended way of doing this:
public static class CustomEnumerator
{
private static int _counter = 0;
private static IEnumerator enumerator;
public static IEnumerable<T> GetRecords<T>(this IEnumerable<T> source)
{
if (enumerator == null) enumerator = source.GetEnumerator();
if (_counter == 0)
{
enumerator.MoveNext();
_counter++;
yield return (T)enumerator.Current;
}
else
{
while (enumerator.MoveNext())
{
yield return (T)enumerator.Current;
}
_counter = 0;
enumerator = null;
}
}
}
Usage:
private static void ReadCsv(IEnumerable<string> records)
{
var headerRecord = records.GetRecords();
var dataRecords = records.GetRecords();
}
Yes, use the IEnumerator
from the IEnumerable, and you can maintain the position across method calls;
A simple example;
public class Program
{
static void Main(string[] args)
{
int[] arr = new [] {1, 2, 3};
IEnumerator enumerator = arr.GetEnumerator();
enumerator.MoveNext();
Console.WriteLine(enumerator.Current);
DoRest(enumerator);
}
static void DoRest(IEnumerator enumerator)
{
while (enumerator.MoveNext())
Console.WriteLine(enumerator.Current);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With