There are times when it\'s helpful to check a non-repeatable IEnumerable
to see whether or not it\'s empty. LINQ\'s Any
doesn\'t work well
You don't need to complicate it. A regular foreach
loop with a single extra bool
variable will do the trick.
If you have
if(input.Any())
{
A
foreach(int i in input)
{
B
}
C
}
and you don't want to read input
twice, you can change this to
bool seenItem = false;
foreach(int i in input)
{
if (!seenItem)
{
seenItem = true;
A
}
B
}
if (seenItem)
{
C
}
Depending on what B
does, you may be able to avoid the seenItem
variable entirely.
In your case, Enumerable.Zip
is a fairly basic function that is easily reimplemented, and your replacement function can use something similar to the above.
Edit: You might consider
public static class MyEnumerableExtensions
{
public static IEnumerable<TFirst> NotReallyZip<TFirst, TSecond>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TFirst> resultSelector)
{
using (var firstEnumerator = first.GetEnumerator())
using (var secondEnumerator = second.GetEnumerator())
{
if (secondEnumerator.MoveNext())
{
if (firstEnumerator.MoveNext())
{
do yield return resultSelector(firstEnumerator.Current, secondEnumerator.Current);
while (firstEnumerator.MoveNext() && secondEnumerator.MoveNext());
}
}
else
{
while (firstEnumerator.MoveNext())
yield return firstEnumerator.Current;
}
}
}
}
This is not an efficient solution if the enumeration is long, however it is an easy solution:
var list = input.ToList();
if (list.Count != 0) {
foreach (var item in list) {
...
}
}
You could also just read the first element and if it's not null, concatenate this first element with the rest of your input:
var input = ReadNumbers();
var first = input.FirstOrDefault();
if (first != default(int)) //Assumes input doesn't contain zeroes
{
var firstAsArray = new[] {first};
foreach (int i in firstAsArray.Concat(input))
{
// Will include the first element.
Console.WriteLine(i);
}
}
For a normal enumerable, the first element would be repeated twice, but for a non-repeatable enumerable it would work, unless iterating twice is not allowed. Also, if you had such an enumerator:
private readonly static List<int?> Source = new List<int?>(){1,2,3,4,5,6};
private static IEnumerable<int?> ReadNumbers()
{
while (Source.Count > 0) {
yield return Source.ElementAt(0);
Source.RemoveAt(0);
}
}
Then it would print: 1, 1, 2, 3, 4, 5, 6. The reason being that the first element is consumed AFTER it has been returned. So the first enumerator, stopping at the first element, never has the chance of consuming that first element. But it would be a case of a badly written enumerator, here. If the element is consumed, then returned...
while (Source.Count > 0) {
var returnElement = Source.ElementAt(0);
Source.RemoveAt(0);
yield return returnElement;
}
...you get the expected output of: 1, 2, 3, 4, 5, 6.