Reputation: 36895
Context:
I have a variable numbers
of type IEnumerable<int>
.
I want to check if numbers are in ascending order.
Algorithm
So I was getting the first element storing it in prev
and wanted to check that against next subsequent numbers.
// numbers can contain "10, 20, 60, 50"
IEnumerable<int> numbers = TraverseInOrder(root);
int prev = numbers.FirstOrDefault();
foreach (var curr in numbers.Skip(1))
{
if (curr < prev) return false;
prev = curr;
}
return true;
Question
I am setting the value of prev
using numbers.FirstOrDefault()
and also skipping one element(numbers.Skip(1)) in foreach
to start from next element.
So for following codes,
numbers.FirstOrDefault()
numbers.Skip(1)
Am I enumerating the numbers
twice O(2N)? Meaning FirstOrDefault
iterate the whole list?
-or-
Is it still O(N)? (O(1) constant time for FirstOrDefault
+ O(N) for Skip(1)
)
Upvotes: 2
Views: 3545
Reputation: 239646
I would subtly change your technique so that you don't need to know (although other answers address what you've specifically asked for):
IEnumerable<int> numbers = TraverseInOrder(root);
int prev = 0;
bool first = true;
foreach (var curr in numbers)
{
if (!first && curr < prev) return false;
prev = curr;
first = false;
}
return true;
Now you know (even without reading the documentation!) that this code will only iterate the enumerable once.
Upvotes: 2
Reputation: 51330
It's a bit subtle.
Enumerable.FirstOrDefault
won't enumerate the whole sequence, as there's no point in doing that. Here's the implementation:
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source) {
if (source == null) throw Error.ArgumentNull("source");
IList<TSource> list = source as IList<TSource>;
if (list != null) {
if (list.Count > 0) return list[0];
}
else {
using (IEnumerator<TSource> e = source.GetEnumerator()) {
if (e.MoveNext()) return e.Current;
}
}
return default(TSource);
}
You get an optimized version if the underlying sequence is an IList<T>
, but otherwise an enumeration will be started.
Now imagine your IEnumerable<T>
is actually an Entity Framework IQueryable<T>
which represents a database query. What this means is that your code will still execute the query twice.
Better avoid enumerating IEnumerable<T>
sequences multiple times as a rule of thumb. If you know that enumerating your sequence is cheap, you should use a more descriptive type, like ICollection<T>
or IReadOnlyCollection<T>
.
Upvotes: 6
Reputation: 1523
FirstOrDefault()
isn't iterating the whole collection.
Just do:
var moreNumbers = numbers.ToList();
// ...stuff with the list.
Upvotes: -1